React useState hook doesn't work as expected - javascript

I have a simple useState hook with which the state of selected checkboxes is managed.
The selected checkboxes should be displayed in the ui (within my filter).
The selected checkboxes are stored correctly, but only displayed when I close my filter component and open it again. Not immediately after a checkbox is checked.
Here is my current state (simplified):
Filter Component:
const [selected, setSelected] = useState([]);
const handleState = (id: string) => {
const selectedIndex = selected.findIndex((i) => i.id === id);
if (selectedIndex > -1) {
selected.splice(checkedIndex, 1);
} else {
selected.push(data.find((i) => i.id === id));
}
// set the state of the selected checkboxes
setSelected(selected);
};
Rendering:
{selected.length > 0 ? (
<div className="selected-boxes">
{selected.map((cb) => (
<span key={cb.label}>{cb.label}</span>
))}
</div>
) : (
// some other stuff

selected is an array that you mutate when some checkbox is checked but referentially this is still the same value so React won't rerender the component.
When you close the filter the component is unmounted and when you open it again it is mounted with the fresh values.
You need to return a new array instead of mutating the current one, for example like this:
// set the state of the selected checkboxes
setSelected(selected.slice());
or
// set the state of the selected checkboxes
setSelected([...selected]);
or use immer that allows you to work with immutable state in a more convenient way.

Related

React Native toggle add/remove element in an array based off toggling a button

I have a TouchableOpacity that can be both pressed and unpressed.
Here is my useState variable:
const [isPressMonday, setIsPressMonday] = useState(false);
and here is my toggling function:
setIsPressMonday(!isPressMonday);
setDays(days => [...days, 'Monday'])
};
The thing is, when a user un-clicks the button, it should remove Monday from the array. This would be simple if I could just check if(!isPressMonday) and remove it from the array, but the default is false so that is almost always true
You don't need to check with isPressMonday state instead check if 'Monday' present in days state. If yes, remove else, add.
//assuming 'days' is your array state name
setIsPressMonday(isPressMonday => !isPressMonday);
const index = days.indexOf('Monday');
if (index !== -1) {
let tempDays = days
tempDays.splice(index, 1);
setDays(tempDays)
}
else {
setDays(days => [...days, 'Monday'])
}

React: useState array not updating

When you push an array, data is pushed. However if you check in console.log, data is not imported. It seems to be a delay. Can you tell me why is this happening?
is there solution for this?
Expected console.log result showing input, however empty array shows and if you click checkbox again then input appears.
const [checked, setChecked] = useState<number[]>([])
const handleAddListToArray = (id: number) => {
console.log(checked)
if (setChecked.includes(id)) {
setChecked(checked.filter((item) => item !== id))
} else {
setChecked([...checked, id])
}
}
--- checkbox compornent ---
const [isChecked, setIsChecked] = useState(false)
const handleChange = () => {
setIsChecked(!isChecked)
handleAddListToArray(id)
}
<Checkbox checked={isChecked} onClick={() => handleChange()} />
when you push an array, data is pushed however if you check in
console.log data is not inported. it seems to be a delay can you tell
me why is this happening?
The state-setting functions are asynchronous. In other words, if you wrote:
const [foo, setFoo] = useState(0);
setFoo(1);
console.log(foo); // logs 0, NOT 1
you would see 0 logged, not 1 ... at least initially. However, there'd be a log entry below that would show 1.
This is because set* function don't change the value in the function, but they do cause the component to be re-rendered after, which means the function is run again, and now uses the correct value..
however empty array shows and if you click checkbox again then input
appears.
It's because of this code:
setIsChecked(!isChecked)
Initially you set isChecked to an array:
setChecked(checked.filter((item) => item !== id))
But then you changed it to !isChecked ... a boolean. Once you change it to a boolean, you can't change it back to an array.
You check the setState-function if it includes the input, on your fourth row of code:
if (setChecked.includes(id))
I believe you want to chech the checked-state instead, like so:
if (checked.includes(id))
Also, consider using the useState-callback when you mutate your state based on the previous one. So instead of:
setChecked(checked.filter((item) => item !== id))
try:
setChecked((prevState) => prevState.filter((item) => item !== id))
You can also use this when you setting your isChecked state. This ensure that you get your latest known state and avoids getting in some wierd states because React updates the state asynchronous.
Some suggestions
if (setChecked.includes(id)) {
should be (setChecked is a function)
if (checked.includes(id)) {
For setChecked(checked.filter((item) => item !== id))
better usage will be
setChecked(prevCheckedValues => prevCheckedValues.filter((item) => item !== id));
This way you will always get the current checked values when you do a setChecked.
Another way to use this is via a useCallback and pass the dependency array as [checked]
If you want to print checked values each time its changed you can use a useEffect (docs) with correct dependency array.
Usage example
useEffect(()=>{
console.log("Checked", checked);
}, [checked])

how to stop rerendering in react on checkbox check?

I have simple list which is dynamically added on add button click. in my list there is a checkbox is also present .so I have an issue when I toggle the checkbox my whole list is re render why ?
let take example I added A,B,C,D in my list when I toggle D checkbox it should only render D item currently it render whole list why ?
here is my code
https://codesandbox.io/s/stupefied-wildflower-gv9be
const Item = ({ text, checked, onCheckedHandler }) => {
console.log(checked, "ssss");
return (
<div className={checked ? "bg" : ""}>
<span>{text}</span>
<input type="checkbox" onChange={e => onCheckedHandler(e, text)} />
</div>
);
};
Every time items changes (whether by adding a new item or checking a value), you are creating a new onCheckedHandler in your App. This propagates down to your Item component. Since the previous onCheckedHandler property is not referentially equivalent to the previous one, it renders (and you see that console log for each item). Memoizing the component alone won't help because a property being passed to it is changing every time.
To get around that, you need to memoize the onCheckedHandler, try this:
const onCheckedHandler = useCallback((e, selectedText) => {
const target = e.target
setItems(items => {
const i = items.findIndex(i => i.text === selectedText);
let obj = items[i];
obj.checked = target.checked;
return [...items.slice(0, i), obj, ...items.slice(i + 1)];
})
}, [setItems])
The you can wrap your Item compoennt with React.memo, and it should work as expected. You'll also need to import the useCallback the same way you import useState

How I can manage the state for 4 Checkbox in functional component in ReactJS?

How I would create a functional component with exactly 4 checkboxes? I have created dynamically mapping an array and I set its value with:
const [checked, setChecked] = useState([false, false, false, false])
and then, with a function I change its state, something like:
const checkboxes = array.map( (el, index) => {
return <Checkbox
checked={checked[index]}
onChange={checkedHandler}/>
}
This is the handler:
const checkedHandler = (event, index) => {
setChecked(...)
//Well, here I don't know how change the particular value of the array...
}
Or I must create a useState for each Checkbox checked state? And how pass the state of this Checkbox to the father component if it was necessary?
You are invoking the checkedHandler wrong way. You have:
onChange={checkedHandler}
And you should have:
onChange={() => {checkedHandler(index)}}
In the handler itself you should copy your current array to prevent its mutation and change the value of the indexed one:
const checkedHandler = (index) => {
const newChecked = [...checked];
newChecked[index] = !checked[index]; // toogle the checkbox
setChecked(newChecked);
}
Assign id to each checkbox like id="1", id="2" and so on...
Then your checkedHandler() function be like:
checkedHandler(event) {
const newChecked = [...checked]
newChecked[parseint(event.target.id) - 1] = true
setChecked(newChecked)
}
There is ALOT of ways you can do this, if you case is simple, you could just shallow copy the array and change only the index you want.
let newChecked = [...checked]
newChecked[index] = value
setChecked(newChecked)
But for more complex cases, please take a look at Correct modification of state arrays in ReactJS

React select default value on render doesn't change the selected input value + title on change?

After a couple of trials i have managed to create a React select dropdown with dynamic data for each sector.
Now, i have a problem that i am not able to create a default value on render.
I get all the dynamic values, but i would like to have one default value for instance: Select... i have managed to add one static, but for some reason when i switch from Select Categories the title stays at: Select...
WerkgeverList = () => {
const werkgeversArray = [];
const AlleWerkgevers = [
...new Set(this.state.opleidingen.map(q => q.opleiding_werkgever)),
];
AlleWerkgevers.map(werkgever => {
return werkgeversArray.push({ title: werkgever, value: werkgever });
});
console.log(werkgeversArray);
return werkgeversArray;
};
Can someone explain what I am doing wrong?
Keep your default value in the state, then add an event listener to track the changes and update the state
<select value={current_value} onChange={() => onChangedValueHandler()} >

Categories

Resources