Working with react inevitable requires you to manage the state of your application at some point, bearest minimum the state of a component you're currently working with.
Recently, I was working on a side project, and I felt like trying something different from the regular
this.state = { email: '', password: '' };
I was trying to login a user with email and password credential, so I felt like wrapping that up into a user object for human readability or better still no reason sake :).
I did this
this.state = {
user: {
email: '',
password: ''
}
};
Then i noticed my form fields were not taking in the inputs, more like its on readOnly mode.
After googling, it occured to me that the state wasn't updating using the regular
onChange(e) {
const { name, value } = e.target;
this.setState({ [name]: value });
Why because the properties (email and password) are nested under the user state property. At first, I was like that shouldn't be much of a problem, calling user.email, user.password
should do the trick right? You guessed right, it didn't and there i was thinking straight i just wanted things to work so I can move on wiht my code.
The Solution
Quick answer:
onChange(e) {
const { user } = { ...this.state };
const currentState = user;
const { name, value } = e.target;
currentState[name] = value;
this.setState({ user: currentState });
}
Long Version:
react's setState doesn't take care of nested properties, in this case email
and password
. So the only way to make changes is to access the parent state object user
whenever a new change occurs to either email
or password
.
What this means is that everytime you type a new character on any of the field, the user state gets to be re-created. This is not optimal if your user object has lots of fields say like a bulky registration form dealing with over 10 fields is not considered ideal.
The above code uses some es6 features, such as the spread operator & Destructuring.
It's a general bad practise to mutate (add/remove elements of the state directly) state in react. states should be recreated if it must change.
The line const { user } = { ...this.state }
does just that using the spread operator(...) to get the current state. Assigned the value returned to currentState variable const currentState = user;
, destructure the emmitted events const { name, value } = e.target;
coming from the input fields to get the name and thier value. Update the currentState with the value of the input currentState[name] = value;
where name is the property of the user state object email
and password
.
Finally, we transpose the user
with the currentState
whenever an update has been successfully made this.setState({ user: currentState });
.
My recommendation
Some people will argue about using nested properties because React is not oriented to work with nested state. Maybe this is correct or not but here's what I think;
I would suggest you avoid using nested state, if you must then endeavor to make it as light as possible because the downside is every tiny piece of change will recreate the parent object, which is not good for performance.
Top comments (8)
This is spot on. I am new to React and I tried doing it like this and it also seems to work:
const initialStateInput = {
cabeceraFamilia: {
familia: '',
direccion: '',
telefonos: '',
email: ''
},
motivoConsulta: '',
fechaHora: '',
corresponsables: [],
}
const [infoAgendamiento, setInfoAgendamiento] = useState(initialStateInput);
const actualizarState = e => {
const nameObjects = e.target.name.split('.');
const newState = setStateNested(infoAgendamiento, nameObjects, e.target.value);
setInfoAgendamiento({...newState});
};
const setStateNested = (state, nameObjects, value) => {
let i = 0;
let operativeState = state;
if(nameObjects.length > 1){
for (i = 0; i < nameObjects.length - 1; i++) {
operativeState = operativeState[nameObjects[i]];
}
}
operativeState[nameObjects[i]] = value;
return state;
}
< input type="text" className="form-control" name="cabeceraFamilia.direccion" placeholder="Dirección" defaultValue={infoAgendamiento.cabeceraFamilia.direccion} onChange={actualizarState} / >
Sorry, my English is not the best but i wanted to share my solution ... your article helped me for it
I've been searching high and low for a solution to this issue and your article hits the nail on the head. I'll also stray from nested state for performance reasons as my app has dozens of forms and dozens of fields per form.
It does seem a bit awkward that state would not support nested state more easily. This should seriously be a consideration in future releases.
React hooks made state management a lot easier, however handling of updates for arrays, objects and nested data still requires a bit of manipulation with data. Check github.com/avkonst/react-use-state-x which makes array, object, nested data state and global state management simple and efficient using React hooks. Disclaimer: I am an author of the lib.
What about the usage of the state argument given in the setState callback?
As is explained in this article in the React docs you should avoid using the rendered state while updating state. Instead, the argument from the setState callback should be used like so:
However doing this with a nested state using variables that are outside of the scope is impossible:
Any ideas how to solve this?
EDIT: meanwhile I will be using a flat state structure in order to adhere to the component update cycle. I am only using static fields for my state so far so it isn't too bad i guess.
Grouping fields in my component will now look something like this:
Dealing with nested state is not good, but we constantly need it.
I always follow the guidelines and update the whole component state instead of only updating one property. immutability is important because you make sure that your code will never break. If something changes when it shouldn't, it's easier to detect when always updating everything.
Literally just ran into this today. Think I'll just make my life easier and not do nested state.
It’s almost impossible to escape if you are working with lots of API’s out there. There could be nested objects like say
Profile: {
First name: ‘’,
Last name: ‘’ }
Totally safer to avoid but it’s a world where everyone does things in different manner. Hi I badly wanna stay away from super nested stuff all the time but I am stuck in that loop