How to setState() in loop using spread operator - reactjs

I'm trying to set new values for several fields in nested object in state using spread operator in loop, but it works only for last field.
I have an array "formFields" with names of fields which values I want to overwrite. I use map() to compare each element in array with field in state and switch it's value to "true". But it change value only for the last field in array - "comment".
constructor() {
super();
this.state = {
fields: {
time: false,
date: false,
quantity: false,
comment: false,
},
}
}
getFormFields() {
const formFields = ["time", "quantity", "comment"];
formFields.map(item => {
this.setState({
...this.state.fields,
[item]: true
})
});
}
What should I do to overwrite values for all the fields I want?

Since you are changing state in a loop, and each state you set contains the original item with only changed, the latest change overrides the previous one. Instead, create a new state object with the change, and then setState the object once:
getFormFields() {
const formFields = ["time", "quantity", "comment"];
this.setState(formFields.reduce((r, item) => ({
...r,
[item]: true
}), {}));
}
btw - If the fields you want to set to true are always the same, you can create the object manually, and set it:
getFormFields() {
this.setState({
time: true,
quantity: true,
comment: true,
});
}

Related

Change Boolean value based on the previous state value

I have created the toggle function where it will change the Boolean value to false. And I am passing that handler function to button, now I am trying to achieve the same by using previous value, the problem I am facing here is I am having a mock data which will have the following structure {[{}]} inside one object I'll have an array inside that I'll have another objects. I have posted the mock and older implementation by selecting only one value from the mock, could any one guide me how to change the boolean value for the mock which I have. Thanks in advance.
const custDetail = {
customers: [
{
name: "Abc",
isCreated: true,
},
{
name: "bcd",
isCreated: true,
},
{
name: "Dec",
isCreated: true,
},
],
};
Code:
const [creatingCust, setCreatingCust] = useState([custDetail])
const custData = [...creatingCust]
custData[0].customers[0].isCreated = false
setCreatingCust(custData)
//trying to use prevState but I am getting undefined
const onClick = () => {
setCreatingCust(prevState => ({isCreated:!prevState.customers[0].isCreated}))
Shallow copy the state, and all nested state, that is being updated. I suggest using the customer name property to match the customer element in the customers array that you want to update. Use Array.prototype.map to create a new array reference.
I suggest also just storing custDetail in the creatingCust state. I don't a reason to nest it in an array.
Example:
const [creatingCust, setCreatingCust] = useState(custDetail);
const onClick = (name) => {
setCreatingCust(prevState => ({
...prevState,
customers: prevState.customers.map(
customer => customer.name === name
? {
...customer,
isCreated: !customers.isCreated
}
: customer
),
}));
};
If you must have creatingCust be an array the process is similar, but instead of shallow copying into a new object you shallow copy into a new array.
const onClick = (name) => {
setCreatingCust(prevState => [{
...prevState[0],
customers: prevState[0].customers.map(
customer => customer.name === name
? {
...customer,
isCreated: !customers.isCreated
}
: customer
),
}]);
};

array declaration in this.state React

**I'm trying to create an array with 5 values which I could use with nameArray[number].
I think that the declaration of the array is wrong but I don't know how I can fix it.
My idea is that: I have 5 buttons, when I click one of this, only one value of the 5 values in the state array change from false to true.
**
constructor(props) {
super(props);
this.state = {
activeButtons: [false, false, false, false, false]
};
}
cliccato = (e) => {
e.preventDefault();
const number = parseInt(e.target.id);
this.setState(
{
activeButtons: !this.state.activeButtons[number],
},
() => {
console.log(" "+ this.state.activeButtons[number]);
}
);
}
You're updating your state's activeButtons with a single boolean value, rather than an updated array.
You need to generate a new array and modify only the relevant element:
const newArray = [...this.state.activeButtons];
newArray[number] = !newArray[number];
this.setState({
activeButtons: newArray,
});
Declaration of the array is fine. You can make it shorter with Array(5).fill(false).
It's setting state part that needs work. In your current code, you are setting the state to the alternate of a boolean value, instead you need to set it to an array.
this.setState(prevState => ({
activeButtons: prevState.activeButtons.map((val, index) => {
if(index === number) {
return !val;
}
return val;
})
}));
Also, using the functional set state form here
It's because you're overwriting activeButtons every time with only one element value. You need to preserve the other values each time you want to update an element.
Using an Object would be a more graceful data structure in this case though.
this.setState(
{
activeButtons: {
...this.state.activeButtons
[number]: !this.state.activeButtons[number]
}
)

React setState variable issue

I'm am using React to set the visbility (isMarkerShown) of a marker when I click a menu item which is handle by my handleMarker(). I get an undefined value if I tried to set the state like how I did below.
state = {
collapsed: false,
visible: false,
marker: {
isMarkerShown: false,
lat: 0,
lng: 0,
},
};
handleMarker() {
this.setState({marker: this.setState({isMarkerShown: true})});
console.log(this.state.marker);
}
It looks like you might be trying to maintain the rest of the marker props while changing isMarkerShown. If that's the case, the following approach may help:
handleMarker() {
this.setState({
marker: {
...this.state.marker,
isMarkerShown: true
}
});
}
setState function is a void function - it does not return anything. So actually if you are assigning it to some variable, it will hold an undefined value.
Just try to set the state directly:
this.setState((prevState) => ({
marker: {
...prevState.marker,
isMarkerShown: true,
},
});
If you need to access value of current/previous state while setting state- like toggling on/off- you can access it directly within the setState call. You can also use functional setState to ensure your state updates happen in order:
this.setState(prevState => ({ marker: { ...prevState.marker, isMarkerShown: !prevState.marker.isMarkerShown } }))

Clearing inputs in React

I have a component in which I create a new post. I have a state used to configure and create a form. The main object in the state is called formControls and inside each element looks something like this:
title: {
elementType: "input",
elementConfig: {
type: "text",
placeholder: "Title"
},
value: "",
validation: {
required: true
},
valid: false,
isTouched: false
}
Then I have a submit handler in which I create a new post and I try to clear the inputs. I m using 2 way binding so I try to clear by looping through state, make copies and update the values for each elements in the formControls : title, author and content like this:
for (let key in this.state.formControls) {
const updatedState = { ...this.state.formControls };
const updatedInput = { ...this.state.formControls[key] };
updatedInput.value = "";
updatedState[key] = updatedInput;
console.log(updatedState);
this.setState({
formControls: updatedState
});
}
The things is that it only clears the last element in the form (textarea). I console logged updatedState and in each iteration it clears the current input but in the next iteration the previous cleared input has again the value before clearing so only the last element is cleared in the end. If i move const updatedState = { ...this.state.formControls };
outside the for loop is behaves as it should. Does this happen because of async operation of setState() and it doesn t give me the right previous state when I try to update in each iteration?
I was hoping that maybe someone could help me understand why is like this. I would post more code but is quite long.
The data available to you in the closure is stale after the first call to setState. All iterations in the for .. in block will be run with the "old" data, so your last iteration is the one which is actually setting the fields to the values as they were when the for .. in loop began.
Try calling setState only once and put all your required changes into that.
const updatedFormControls = { ...this.state.formControls };
for (let key in this.state.formControls) {
const updatedInput = { ...updatedFormControls[key] };
updatedInput.value = "";
updatedFormControls[key] = updatedInput;
}
console.log(updatedFormControls);
this.setState({
formControls: updatedFormControls
});
Another way to do the same thing might look like this:
this.setState(state => ({
...state,
formControls: Object.keys(state.formControls).reduce(
(acc, key) => ({
...acc,
[key]: { ...state.formControls[key], value: '' }
}),
{}
)
});

React JS - How to set state of variable inside variable?

I want to set state of this form :
this.state = {
filter: {
search: null,
brands: null,
price: null
}
}
How to set value for search / brands / price ?
Do the following:
this.setState({
filter: {
search: 'value',
brands: 'value',
price: 'value'
}
})
The key is that you don't want to ever mutate a value in state. As a result, you must copy the filter object before passing it to setState. Example:
onSearchChange(value) {
this.setState((state) => {
return {
filter: {
...state.filter,
search: value
}
})
}
Note that I am passing a function to setState. Since the next value of state relies on the previous value, you want to use an updater functions, as the setState docs recommend.
In general, it is nicer if you can keep your state flat. So rather than having a filter object in state, your shape could just be
this.state = {
search: null,
brands: null,
price: null,
}
In which case the above onSearchChange function would just be
onSearchChange(value) {
this.setState({search: value})
}
Definitely a lot easier on the eyes.
I recommend avoiding nested objects and keeping your state flat. e.g.
this.state = {
brandsFilter: null,
priceFilter: null,
searchFilter: null,
};
Component state in react is really nothing more than simple key-value pairs; that's what setState supports. Sure you can have nested objects if you really want. But as the other answers demonstrate, supporting such an approach can be needlessly complex.
you should use the setState function, you can set the filter with updated data like so
const newFilter = {...};
this.setState({filter: newFilter});
You should avoid to mutate React state directly, there are some functions can do immutable jobs for you (ex Object.assign):
const currentFilter = this.state.filter;
const newFilter = Object.assign({}, currentFilter, {search: "new search", brands: [], price: 100});
this.setState({filter: newFilter});
ES 6:
const currentFilter = this.state.filter;
this.setState({
filter: {
...currentFilter,
search: "new search",
brands: []
}
});
this.setState({
filter: {
...this.state.filter,
search: "new search",
brands: 'value',
price: 'value'
}
});
You may try this with Spread Operator.
If you need to preserve the previous filter value.
other wise you can,
this.setState({
filter: {
search: "new search",
brands: 'value',
price: 'value'
}
});
let newfilter = Object.assign({}, this.state.filter)
newfilter.search ="value";
newfilter.brands ="value";
newfilter.price ="value";
this.setState({
filter:newfilter
})
You can access the search, brands, price by using:
this.setState({
filter.search = true,
filter.brands = 'stackoverflow',
filter.price = 1400
})
and to access it, just like usual state access (this.state.filter.search).

Resources