React - add and remove from state with checkbox - reactjs

I have a demo here
It's a simple React app with a checkbox.
I'd like to add something to state when the checkbox is selected and then remove it from the state when it's unselected.
I have it working when it's selected but how do I remove it from the state when unselected.
handleChange = (e) => {
const array = [...this.state.colors];
const index = array.indexOf(e.target.name)
if (index !== -1) {
this.setState({colors: this.state.colors.filter(() => {
color !== e.target.name
})})
}else{
this.setState({colors: e.target.name})
}
}

You can access the state of a checkbox with event.target.checked.
Previous state can be accessed with callback version of setState
Docs
handleChange = (e) => {
const checked = e.target.checked;
const selectedColor = e.target.name;
if(checked) {
this.setState(prevState => ({
colors: [...prevState.colors, selectedColor]
}));
} else {
this.setState(prevState => ({
colors: prevState.colors.filter(color => color!==selectedColor)
}));
}
}
Stackblitz

Related

How to update the previous properties of state after onClick event?

So I want to update the previous properties of my state with array of objects.Initially I made only two objects.I want to implement bookmark functionality in my moviedb app.When I click on the icon it should change color as well as change the value of isFavorited property in my useState.
const [isActive, setActive] = useState({
favorited: false,
customColor: "white"
});
const handleToggle = () => {
setActive({ ...isActive, !favorited});
if(isActive.favorited){
setActive( {...isActive, customColor: "red"});
}else{
setActive( {...isActive, customColor: "white"});
}
}
I am calling the handleToggle function on clicking a icon.How to make it so that the value of favorited toggles everytime I click it using useState?I get a error squiggle on !favorited
Writing {...isActive, !favorited} is not a valid JS syntax.
Try rewriting the function to:
const handleToggle = () => {
setActive((prevState) => {
return {
...prevState,
favorited: !prevState.favorited,
customColor: prevState.favorited ? "red" : "white",
};
});
};
You should update the state by using the prevState :-
setActive((prevState) => ({
...prevState, favorited : !favorited}
));

react state not updating on input change

I have an array of objects in a useState hook with which i rendered input elements.
The state doesn't get updated when the input element changes.
Here Is My Code.
ProceduralResponseAnswerCreate Component
const ProceduralResponseAnswerCreate = () => {
const [proceduralAnswers, setProceduralAnswers] = useState([{ "value": "One" }, { "value": "two" }])
return <>
<ol>
{proceduralAnswers.map((answer, answer_index) => <input key={answer_index} style={inputStyle} onChange={(event) => updateAnswerValue({ event, setProceduralAnswers, answer_index })} value={answer.value} />)}
</ol>
</>
}
export default ProceduralResponseAnswerCreate
updateAnswerValue function
export const updateAnswerValue = ({ event, setProceduralAnswers, answer_index }) => {
setProceduralAnswers(state => {
var newState = state;
var currentAnswer = newState.filter((state_1, index) => index === answer_index)[0]
currentAnswer.value = event.target.value
return newState
})
}
I think it doesn't work because the next state has the same Reference as the previous state, you only change the value of one element. The useState hook will trigger a rerender, if the reference changes. So your Callback must look like this.
setProceduralAnswers(state => {
let newState = state;
let currentAnswer = newState.filter((state_1, index) => index === answer_index)[0]
currentAnswer.value = event.target.value
return [...newState]
})
try this
export const updateAnswerValue = ({ event, setProceduralAnswers, answer_index }) => {
setProceduralAnswers(state => {
var newState = state;
newState[answer_index].value = event.target.value
return newState
})
}
You are returning the newState instead of the currentAnswer.
First off, if you're only trying to find one item use [].find not filter.
var newState = state; does not give you a fresh copy, change newState, and you'll be changing state too, you need to do var newState = [...state]; (warning: this is a shallow copy only).
It looks like you think currentAnswer shares a reference with newState in some way, which it doesn't. Whenever you run map, find, filter or reduce you're getting a fresh copy of the array so there will not be a link between the 2.
A good bet here is to just map the state
export const updateAnswerValue = ({
event,
setProceduralAnswers,
answer_index
}) => {
setProceduralAnswers((state) => state.map((state_1, index) => {
if (index === answer_index) {
return {
...state_1,
value: event.target.value
}
}
return state_1;
}));
};

React useState hook - update state of a object

I have a checklist object state
const [checkList, setCheckList] = useState({checkA, checkB .. 100 checks})
How to update all states to e.target.checked at once? I should be able to update like this:
const handleAllCheck = (e) => {
Object.keys(checkList).map((resource) => {
setCheckList({ ...checkList, [resource]: e.target.checked });
});
};
But this updates only one of 100 checks, What am I missing?
Issues
You are enqueueing a bunch of state updates in a loop but not using a functional state update, so the same state from the current render cycle the updates are enqueued in is used for each update, overwriting the previous update. The last enqueued update wins and is the state set for the next render cycle.
const handleAllCheck = (e) => {
Object.keys(checkList).map((resource) => {
setCheckList({
...checkList, // <-- state from the previous render cycle
[resource]: e.target.checked,
});
});
};
Solution
In order to update all the checked values you can simply forEach over the keys and use a functional state update to spread in the state from the previous enqueued update.
const handleAllCheck = (e) => {
const { checked } = e.target;
Object.keys(checkList).forEach((resource) => {
setCheckList(checkList => ({
...checkList, // <-- state from the previous update
[resource]: checked,
}));
});
};
Alternatively you can use a single update by reducing the old state into a new state object.
const handleAllCheck = (e) => {
const { checked } = e.target;
setCheckList(checkList => Object.keys(checkList).reduce(
(checkList, resource) => ({
...checkList,
[resource]: checked,
}),
{},
));
};
can you try
const handleAllCheck = (e) => {
const newCheckList = Object.keys(checkList).map((resource) => {
return e.target.checked;
});
setCheckList(newCheckList);

update the state object after the event tigger using functional component react

I' m using the useContext hook to manage the state, i have tigger the handleChange method from the other component's input slider field, this changes the state object and that state are used to filter the data
this is the context API hook component
handleChange is tigger by the other component input price slider
const handleChange = (event) => {
const target = event.target;
const name = target.name;
const value = target.value;
setState(
{
...state,
[name]: value,
},
**Part 1
filterBooks()
);
};
const filterBooks = () => {
let { books, maxPrice, minPrice, type, category, price } = state;
let tempBook = [...books];
price = parseInt(price);
// console.log(typeof price);
// console.log(tempBook, price);
if (type !== "all") {
tempBook = tempBook.filter((book) => book.type === type);
}
tempBook = tempBook.filter((book) => book.price < price);
**Part 2 **
*code works as expected till here*
setState({
...state,
sortedBook: tempBook,
});
// how to update state in this scenario;
// as per the docs we can't update state in nested function
};
return (
<ProductContext.Provider value={{ state, handleChange }}>
{props.children}
</ProductContext.Provider>
);
};
export default ProviderContext;```
Not sure why you're filtering in the setState callback, why not just update the filter in the initial setState?
Also, unless your state is deeply nested you shouldn't need to pass the full state to the setState, and if it is nested - I'd consider a redux implementation
I refactor the code and it worked
const handleChange = (event) => {
const target = event.target;
const name = target.name;
const value = target.value;
setFlag(false);
setState({
...state,
[name]: value,
sortedBook: [],
});
setFlag(true);
};
useEffect(() => {
if (!flag) {
// console.log("effect called without change - by default");
} else {
// console.log("effect called with change ");
const filterBooks = () => {
let { books, maxPrice, minPrice, type, category, price } = state;
let tempBook = [...books];
if (type !== "all") {
tempBook = tempBook.filter((book) => book.type === type);
}
tempBook = tempBook.filter((book) => book.price < parseInt(price));
// console.log("from tempbook", tempBook);
setState({
...state,
sortedBook: tempBook,
});
};
filterBooks();
}
// console.log(state);
}, [state.price, flag]);

is there a way to shorten this change handler that uses hooks in React?

With class based components you could use the computed properties to in order to use one single handler for several inputs with the help of an id attribute like this:
function handleChange(evt) {
const value = evt.target.value;
setState({
...state,
[evt.target.name]: value
});
}
This is thebest I caome up with but im sure there must be a shorter way:
const handleChange = (e) => {
if (e.target.id === 'name') setName(e.target.value)
if (e.target.id === 'genre') setGenre(e.target.value)
if (e.target.id === 'description') setDescription(e.target.value)
}
Any ideas?
Thanks.
You could have create an object in the useState and update just like the first example.
// Declare the state with a object default value
const [state, setState] = useState({})
const handleChange = (e) => {
setState(prev => ({
...prev,
[e.target.id]: e.target.value
}))
}
If you can't change the useState and have a very strong reason to have these 3 separeted useStates, you could create an object that returns the 3 functions based on the e.target.id
const updater = {
name: setName,
genre: setGenre,
description: setDescription,
}
And when updating you do
const handleChange = (e) => {
const updateValue = updater[e.target.id]
if (updateValue) {
updateValue(e.target.value)
}
else {
// No function to update found
// Do something else
}
}

Resources