I\'m trying to organize my state by using nested property like this:
this.state = {
someProperty: {
flag:true
}
}
But updating
If you are using formik in your project it has some easy way to handle this stuff. Here is the most easiest way to do with formik.
First set your initial values inside the formik initivalues attribute or in the react. state
Here, the initial values is define in react state
state = {
data: {
fy: {
active: "N"
}
}
}
define above initialValues for formik field inside formik initiValues
attribute
<Formik
initialValues={this.state.data}
onSubmit={(values, actions)=> {...your actions goes here}}
>
{({ isSubmitting }) => (
<Form>
<Field type="checkbox" name="fy.active" onChange={(e) => {
const value = e.target.checked;
if(value) setFieldValue('fy.active', 'Y')
else setFieldValue('fy.active', 'N')
}}/>
</Form>
)}
</Formik>
Make a console to the check the state updated into string
instead of boolean
the formik setFieldValue
function to set the state or go with react debugger tool to see the changes iniside formik state values.
To write it in one line
this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
Sometimes direct answers are not the best ones :)
Short version:
this code
this.state = {
someProperty: {
flag: true
}
}
should be simplified as something like
this.state = {
somePropertyFlag: true
}
Long version:
Currently you shouldn't want to work with nested state in React. Because React is not oriented to work with nested states and all solutions proposed here look as hacks. They don't use the framework but fight with it. They suggest to write not so clear code for doubtful purpose of grouping some properties. So they are very interesting as an answer to the challenge but practically useless.
Lets imagine the following state:
{
parent: {
child1: 'value 1',
child2: 'value 2',
...
child100: 'value 100'
}
}
What will happen if you change just a value of child1
? React will not re-render the view because it uses shallow comparison and it will find that parent
property didn't change. BTW mutating the state object directly is considered to be a bad practice in general.
So you need to re-create the whole parent
object. But in this case we will meet another problem. React will think that all children have changed their values and will re-render all of them. Of course it is not good for performance.
It is still possible to solve that problem by writing some complicated logic in shouldComponentUpdate()
but I would prefer to stop here and use simple solution from the short version.
We use Immer https://github.com/mweststrate/immer to handle these kinds of issues.
Just replaced this code in one of our components
this.setState(prevState => ({
...prevState,
preferences: {
...prevState.preferences,
[key]: newValue
}
}));
With this
import produce from 'immer';
this.setState(produce(draft => {
draft.preferences[key] = newValue;
}));
With immer you handle your state as a "normal object". The magic happens behind the scene with proxy objects.
Create a copy of the state:
let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))
make changes in this object:
someProperty.flag = "false"
now update the state
this.setState({someProperty})
I know it is an old question but still wanted to share how i achieved this. Assuming state in constructor looks like this:
constructor(props) {
super(props);
this.state = {
loading: false,
user: {
email: ""
},
organization: {
name: ""
}
};
this.handleChange = this.handleChange.bind(this);
}
My handleChange
function is like this:
handleChange(e) {
const names = e.target.name.split(".");
const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;
this.setState((state) => {
state[names[0]][names[1]] = value;
return {[names[0]]: state[names[0]]};
});
}
And make sure you name inputs accordingly:
<input
type="text"
name="user.email"
onChange={this.handleChange}
value={this.state.user.firstName}
placeholder="Email Address"
/>
<input
type="text"
name="organization.name"
onChange={this.handleChange}
value={this.state.organization.name}
placeholder="Organization Name"
/>