Change a checkbox checked state from a useEffect hook in React? - reactjs

The value of context.number is 1. I want to set the input checked value to true based off the context.number. So if context.number is 1, the checkbox is checked for Module 1, showing that Module 1 is completed. I can only change the input value from the onChange event though, so i assume i need a useEffect hook so it does it automatically when context.number changes?
import React, { useState, useContext, useEffect } from "react";
import { Link } from "react-router-dom";
import { AppContext } from "../context/AppContext";
export default function Menu() {
const context = useContext(AppContext);
//Determine the modules
const modules = [
{
title: `Introduction`,
subtitle: "Lesson 1",
},
{
title: `Overview`,
subtitle: "Lesson 2",
}
];
//Create a checked state for each module
const [checkedState, setCheckedState] = useState(new Array(modules.length).fill(false));
//Change checked value method
const handleOnChange = (position) => {
//map through checked states array, if position === mapped item index, flip the value
const updatedCheckedState = checkedState.map((item, index) => (index === position ?
!item : item));
//set that items state to the new value in the array
setCheckedState(updatedCheckedState);
};
return (
<>
<div>
{modules.map((module, index) => (
<div className={styles.menuCard}>
<Link key={index} to={`/Module/${index + 1}`}>
<h2>{module.title}</h2>
<p>{module.subtitle}</p>
</Link>
<input
id={`check${index}`}
type="checkbox"
onChange={() => handleOnChange(index)}
checked={checkedState[index]}
/>
</div>
))}
</div>
</>
);
}

I put this is my context file and was able to get it to work. This is a poorly worded question I realize but found my answer none the less. useEffect wasn't the problem, it was that each checkbox was only saving local state so if i rendered another page the checkboxes went back to being unchecked.
import React, { createContext, useState } from "react";
const AppContext = createContext();
function AppProvider(props) {
//Determine the modules
const modules = [
{
title: `Introduction`,
subtitle: "Lesson 1",
},
{
title: `Overview`,
subtitle: "Lesson 2",
}
];
//set module number state
const [number, setNumber] = useState();
//Create a checked state for each module
const [checkedState, setCheckedState] = useState(new Array(modules.length).fill(false));
//change module number method
function completionHandler(value) {
setNumber(value);
}
//Change checked value method
function handleChange(position) {
const updatedCheckedState = checkedState.map((item, index) => (index == position ? !item : item));
setCheckedState(updatedCheckedState);
}
//export method and state value
const value = {
number: number,
modules: modules,
checkedState: checkedState,
handleChange: handleChange,
completionHandler: completionHandler,
};
return <AppContext.Provider value={value}>{props.children}</AppContext.Provider>;
}
export { AppContext, AppProvider };

Related

Maintaining multiple user inputs to antd Select in a single useState variable

I want to store the information of multiple inputs entered into antd Select components in a single state variable but am having trouble getting the below to work.
This example is solved here for a form but the same solution doesn't seem to work for antd Select component. There are two inputs: a first name and a last name that I want to remember. The below code doesn't work because e doesn't have an attribute called name is what the console tells me. I also tried e.target.name and e.target.value but I get an error that e doesn't have an attribute called a target either. What is the right way to do this?
import React, { useState } from 'react';
import { Select } from 'antd';
const App = () =>{
const [varState, setVarState] = useState({firstName:'Jack', lastName:'Smith'});
const firstNameOptions = [ {label:'Jack', value:'Jack'}, {label:'Jill',value:'Jill'}, {label:'Bill',value:'Bill'} ];
const lastNameOptions = [ {label:'Smith', value:'Smith'}, {label:'Potter',value:'Potter'}, {label:'Bach',value:'Bach'} ];
const changeState = (e) => {
setVarState( prevState => ({ ...prevState, [e.name]: e.value}));
console.log(varState)
};
return ( <>
<div>
<Select name={'firstName'} defaultValue={'Pick One'} options={firstNameOptions} onChange={changeState} />
<Select name={'lastName'} defaultValue={'Pick One'} options={lastNameOptions} onChange={changeState} />
</div>
</>
);
}
export default App;
At the heart of it, it seems that I don't know how to name the Select components in such a way that their names can be passed on to the onChange handler.
More generally, given a component like antd Select, how can I figure out what the right "name field" is for this component so that it's value can be passed on to an onChange handler? For instance, what in the documentation for select gives this information?
The Select component seems to be sending the value instead of the events object. So, You can just make a closure and pass the name of the select. Also, for consoling you can make use of a useEffect which only consoles when the state has been updated. Otherwise, you could see previous state as state updates are asynchronous. Below is a working solution.
import React, { useEffect, useState } from "react";
import { Select } from "antd";
const App = () => {
const [varState, setVarState] = useState({
firstName: "Jack",
lastName: "Smith"
});
const firstNameOptions = [
{ label: "Jack", value: "Jack" },
{ label: "Jill", value: "Jill" },
{ label: "Bill", value: "Bill" }
];
const lastNameOptions = [
{ label: "Smith", value: "Smith" },
{ label: "Potter", value: "Potter" },
{ label: "Bach", value: "Bach" }
];
// for consoling when the state updates
useEffect(() => {
console.log(varState);
}, [varState.firstName, varState.lastName]);
const changeState = (value, identifier) => {
// console.log(value, identifier);
setVarState((prevState) => ({ ...prevState, [identifier]: value }));
};
return (
<>
<div>
<Select
name={"firstName"}
defaultValue={"Pick One"}
options={firstNameOptions}
onChange={(val) => changeState(val, "firstName")}
/>
<Select
name={"lastName"}
defaultValue={"Pick One"}
options={lastNameOptions}
onChange={(val) => changeState(val, "lastName")}
/>
</div>
</>
);
};
export default App;
yes, Actually antd doesn't have attribute name for input fields. antdesign directly gives the selected value, we need to do some tweeks to achieve this.
Here is the solution:
import React, { useState } from 'react';
import { Select } from 'antd';
const firstNameOptions = [ {label:'Jack', value:'Jack'}, {label:'Jill',value:'Jill'}, {label:'Bill',value:'Bill'} ];
const lastNameOptions = [ {label:'Smith', value:'Smith'}, {label:'Potter',value:'Potter'}, {label:'Bach',value:'Bach'} ];
const App = () =>{
const [varState, setVarState] = useState(null);
const changeState = (fieldName) => (value) => {
setVarState( prevState => ({ ...prevState, [fieldName]: value}));
console.log(varState)
};
return ( <>
<div>
<Select defaultValue={'Pick One'} options={firstNameOptions} onChange={changeState('firstName')} />
<Select defaultValue={'Pick One'} options={lastNameOptions} onChange={changeState('lastName')} />
</div>
</>
);
}
export default App;
I hope this helps 😊

react quill lose focus onChange

hello everyone i dont know why i lose my focus, i have seen it could be linked with rerendering but i have no idea what to do
hello everyone i dont know why i lose my focus, i have seen it could be linked with rerendering but i have no idea what to do
1) i tried to useRef but it always focus at the begining of the textarea
2) i used React Quill library so as i know its common mistake
3) please help
import React from "react";
import Editor from "./Editor";
import { initialState } from "../redux/reducer/field";
import { connect } from "react-redux";
import ReactQuill, { Quill } from "react-quill";
import { getValue } from "#testing-library/user-event/dist/utils";
import { useRef, useEffect } from "react";
import { useState } from "react";
import "quill-mention";
import "react-quill/dist/quill.snow.css";
const EditorFirstComp = ({
setContent,
content,
fieldList,
setDataFromEditor,
}) => {
const atValues = fieldList.map(
(field) => [{ id: field.key, value: field.label }][0]
);
const hashValues = [
{ id: 3, value: "Fredrik Sundqvist 2" },
{ id: 4, value: "Patrik Sjölin 2" },
];
const mentionModuleConfig = {
allowedChars: /^[A-Za-z\sÅÄÖåäö]*$/,
mentionDenotationChars: ["#", "#"],
source: function (searchTerm, renderList, mentionChar) {
let values;
if (mentionChar === "#") {
values = atValues;
} else {
values = hashValues;
}
if (searchTerm.length === 0) {
renderList(values, searchTerm);
} else {
const matches = [];
for (let i = 0; i < values.length; i++)
if (~values[i].value.toLowerCase().indexOf(searchTerm.toLowerCase()))
matches.push(values[i]);
renderList(matches, searchTerm);
}
},
};
const modules = {
mention: mentionModuleConfig,
};
const Editor = ({ setContent, setDataFromEditor, content, modules }) => {
const [local,setLocal]=useState(content)
const handleChange = (e) => {
setContent(e);
};
// const elemRef=useRef(null)
// useEffect(()=>{
// elemRef.current.focus()
// },[content])
return (
<form>
<ReactQuill
theme="snow"
defaultValue={content}
onChange={(e) => handleChange(e)}
modules={modules}
/>
</form>
);
};
return (
<Editor
setContent={setContent}
content={content}
modules={modules}
></Editor>
);
};
const mapStateToProps = (state) => ({
fieldList: state.field,
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(mapStateToProps, mapDispatchToProps)(EditorFirstComp);
Because you creating Editor component inside another component, every render react will create new Editor component and therefore there is no longer old editor which have focus in. Declare Editor component outside of EditorFirstComp or wrap declaration of Editor inside of useMemo, to memoize this instance.

Trying to pass selected value from dropdown and substituting in path for redirection

import React, {useState} from 'react';
import Select from 'react-select';
import { useHistory } from "react-router-dom";
function Cluster()
{
var clusters= [
{
value:1,
label: "NA1"
}
];
const history = useHistory();
const [result,ddlvalue]= useState(clusters.label);
const routeChange = e =>{
ddlvalue(e.label);
let path = 'inventory/cluster/'+result;
console.log(path)
history.push(path);
}
return(
<div>
<Select options={clusters} onChange={routeChange}/>
<h1>{result}</h1>
</div>
)
}
export default Cluster;
console.log(path) is giving me inventory/cluster/undefined, i am a beginner in react and facing this issue. Looking for some guidance.
React state updates are asynchronously processed, so you can't enqueue a state update and expect to use the updated result state value within the same render cycle/callback scope. Just use the current label value from the onChange event to form the next target path value. Add a leading "/" so the link is treated as an absolute path, i.e. "/inventory/cluster/" + e.label.
function Cluster() {
var clusters = [
{
value: 1,
label: "NA1"
},
{
value: 2,
label: "NA2"
},
{
value: 3,
label: "NA3"
},
{
value: 4,
label: "NA4"
}
];
const history = useHistory();
const [result, ddlvalue] = useState(clusters[0].label);
const routeChange = (e) => {
ddlvalue(e.label);
let path = "/inventory/cluster/" + e.label;
console.log(path);
history.push(path);
};
return (
<div>
<Select options={clusters} onChange={routeChange} />
<h1>{result}</h1>
</div>
);
}

React Multiple Checkboxes are not visible as selected after change

I have a list of checkboxes.
Checkbox is not visible as selected even after the value has been changed.
Below is my code: -
import React, { useState } from "react";
import { render } from "react-dom";
const CheckboxComponent = () => {
const [checkedList, setCheckedList] = useState([
{ id: 1, label: "First", isCheck: false },
{ id: 2, label: "Second", isCheck: true }
]);
const handleCheck = (e, index) => {
checkedList[index]["isCheck"] = e.target.checked;
setCheckedList(checkedList);
console.log(checkedList);
};
return (
<div className="container">
{checkedList.map((c, index) => (
<div>
<input
id={c.id}
type="checkbox"
checked={c.isCheck}
onChange={e => handleCheck(e, index)}
/>
<label htmlFor={c.id}>{c.label}</label>
</div>
))}
</div>
);
};
render(<CheckboxComponent />, document.getElementById("root"));
I was working fine for a simple checkbox outside the loop.
I am not sure where is the problem.
Here is the link - https://codesandbox.io/s/react-multiple-checkboxes-sczhy?file=/src/index.js:0-848
Cause you pass an array to the state, so if you want your react component re-render, you must let the react know that your state change. On your handleCheck, you only change property of an value in that array so the reference is not changed.
The handleCheck function should be look like this
const handleCheck = (e, index) => {
const newCheckList = [...checkedList];
newCheckList[index]["isCheck"] = e.target.checked;
setCheckedList(newCheckList);
};
Do this instead:
const handleCheck = (e, index) => {
setCheckedList(prevState => {
const nextState = prevState.slice()
nextState[index]["isCheck"] = e.target.checked;
return nextState
});
};
Since checkedList is an array, (considered as object and handled as such), changing a property won't change the array itself. So React can know that something changed.

React Function Components - following parent component state

I'm new to react and I'm trying component functional style.
I have simple todo list. I would like to strike out todo item from list using style property. From Chrome debug mode I do not see immediate reaction on checkbox changes, also Item is not striked out... It seams to me, that it is problem with how I manage state of components. I would appreciate some guidance.
App.js
import React, {useState} from 'react';
import Todos from "./components/Todos";
import './App.css'
const App = () => {
const [todos, setTodos] = useState(
[
{id: 1, title: 'Take out the trash', completed: false},
{id: 2, title: 'Dinner with wife', completed: false},
{id: 3, title: 'Meeting boss', completed: false}
]
);
const markComplete = id => {
console.log((new Date()).toString());
todos.map(todo => {
if (todo.id === id) {
todo.completed = ! todo.completed;
}
return todo;
});
setTodos(todos);
};
return (
<div className="App">
<Todos todos={todos} markComplete={markComplete}/>
</div>
);
};
export default App;
Todos.js
import React from "react";
import TodoItem from "./TodoItem";
const Todos = ({todos, markComplete}) => {
return (
todos.map(todo => (
<TodoItem key={todo.id} todoItem={todo} markComplete={markComplete} />
))
);
};
export default Todos;
TodoItem.js
import React from "react";
const TodoItem = ({todoItem, markComplete}) => {
const getStyle = () => {
console.log("style: " + todoItem.completed);
return {
background: '#f4f4f4',
padding: '10px',
borderBottom: '1px #ccc dotted',
textDecoration: todoItem.completed ? 'line-through' : 'none'
}
};
return (
<div style={getStyle()}>
<p>
<input type="checkbox" onChange={markComplete.bind(this, todoItem.id)}/>{' '}
{todoItem.title}
</p>
</div>
);
};
export default TodoItem;
I expect that this getStyle() will follow state... somehow...
Don't mutate state. In markComplete function, you are mutating the todos array directly. Change your function like this to avoid mutation
const markComplete = id => {
console.log((new Date()).toString());
let newTodos = todos.map(todo => {
let newTodo = { ...todo };
if (newTodo.id === id) {
newTodo.completed = !newTodo.completed;
}
return newTodo;
});
setTodos(newTodos);
};
Array.prototype.map() returns a new Array, which you are throwing away. You need to use the new array, e.g.:
const markComplete = id => {
...
setTodos(totos.map(...))

Resources