Contents
- Introduction
- Updating object that's in state
- Creating an Up vote function
- Implementing The Update Votes Button
- Showcasing mutation
- One Command for Updating Object in state
- Simple Way To Copy JavaScript object For easy Property update
- Conclusion
Introduction
One of the fundamental concepts of the React framework is state management. State in react can hold any kind of JavaScript data types ranging from the primitive data types like string, number and boolean to the complex data types like objects.
There are many ways to introduce state into your component, one of the many ways is using the hook: useState
. Typically, the useState hook allows us to initialize the state variable.
const newStudent = {
name:'Jane Doe',
age: 15
}
const [student, setStudent] = useState(newStudent);
On the other hand, an Object in JavaScript is a real world entity that contains properties and or type. An example of an object can be as put in the above code; a student with properties name, age.
Further we can also have an array of objects which is fairly common in many applications
//an array of song objects with properties like title and votes
const songList = [
{
id: 0,
title: "\"All for you\","
votes: Math.floor(Math.random() * 5 + 1),
},
{
id: 1,
title: "\"Always win\","
votes: Math.floor(Math.random() * 5 + 1),
},
{
id: 2,
title: "\"Grateful\","
votes: Math.floor(Math.random() * 6 + 2),
},
];
The song list array is comprised of objects and each object is made up of 3 properties namely; id, title and votes.
We are using Math.Random()
function to prepopulate the votes
property that is in our song object, this will help us to have initial votes on the first render. You can read more about Math.random() here
Updating object that's in state
Just as any other state in react, an object that's in state is also subject to change, however there are precautions or standards that must be met when updating an object that's in state. In this article I'll help you to update an object in state in a more clean way.
For example for us to make the above songList array of objects editable or updatable we need to put it in state inside a component.
const [songs, setSongs] = useState(songList);
if we can provide a user interface for the array of objects above and traverse the songList
array that is above so that a user should be able to vote for a song that he wishes it may look like this.
return (
<ul className="song-list">
//the map array function will help us to traverse through the songs array
{songs.map((song) => (
<li key={song.id} className="song-item">
<p className=""><span className="votes">{song.votes}</span> votes</p>
<div className="title">{song.title}</div>
<div className="caret">
<BiCaretUp size={24} />
<span className="text">Vote</span>
</div>
</li>
))}
</ul>
);};
and suppose after styling the output looks like this.
This simple UI will allow any user to click on the preferred song and vote for it countless times ( not applicable in real world applications ).
Creating an Up vote function
For us to update the vote after user clicks on the vote button (caret) we need to define a function that will do this work for us. So lets define a function onVote()
that will be called whenever a user clicks the vote caret.
The function will receive a song ID for the clicked song and will
const onVote=(id)=>{
//code for updating the votes the chosen song
}
and then we will add an onClick()
attribute on our button in our UI to capture any clicks on our vote button
...
// adding the onClick function
<div
onClick={()=>onVote(id)}
className="caret">
<BiCaretUp size={24} />
<span className="text">Vote</span>
</div>
....
Whenever a user clicks on this div an the song ID will be passed to this onVote()
.
Implementing The Update Votes Button
There are many ways how we can update the votes after user clicks the vote button. But one thing we have to do is avoid something called mutation.
The major concept to grab when updating object in state is that: An object once created, it never changes , and its variable should never be reassigned. The term mutation refers to
the situation that a value is changed in-place updating a value stored somewhere in memory. In React, state is considered to be an immutable object, meaning that you should not directly change the state object.
Showcasing Mutation
For example, to implement the onVote()
function one can do this just to update the votes in a single specified object.
const onVote = (id) => {
songs.map((song) => {
if (song.id === id) {
//incrementing the votes of the specified song after user clicks
song.votes = song.votes+1
}
});
};
The situation above portrays the concept of mutation. We are directly accessing the song.votes
and incrementing or in other words changing it directly and while its in memory. Respectively this will not work or it may either bring an error. However, there is a specific way to do this without complications.
One Command for Updating Object in state
There is only one rule that works and never fails whenever you want to update object in state.
What this means is that, to avoid mutation You have to create a new Object that will take the contents of the object that wants to be modified and modify the field that you want. In other words whenever you want to update an object in state copy the contents of the object on a new one and then update the object and the update state.
This may look something like this:
const onVote = (id) => {
const newSongs = songs.map((song) => {
if (song.id === id) {
const nextSong = {
id:song.id,
title: "song.title ,"
votes:song.votes+1
}
return nextSong
} else {
return song;
}
});
setSongs(newSongs);
};
First, we use map()
to traverse the songs array. Importantly, map()
returns a new array as opposed to modifying the array song
.
Next, as we traverse we check if the current song ID matches songID. If it does, we create a new object, copying over the properties from the original product object. We then overwrite the votes property on our new product object. We set it to the incremented vote count. So easy.
Her we can see using the above GIF that the votes are incrementing easily after we. REMEMBER! we are only modifying the votes property in our state. You can update many fields depending on the type of application you are developing.
Simple Way To Copy JavaScript object For easy Property update
As we have seen in the recent section that you first of all have to copy the object into whole new object,
const newSongs = songs.map((song) => {
if (song.id === id) {
const nextSong = {
//copying the song object values unto a new object
id:song.id,
title:song.title,
votes:song.votes+1
}
hoever this can become so tiresome if the object can be so big that is when it has so any properties with it. suppose the object looks like this:
const song ={
id: 0,
title: "All for you",
votes: Math.floor(Math.random() * 5 + 1),
duration: "5 min",
type: "mp3",
size: '5MB',
available_online:true,
artist:{
name:'Jones Boy',
adress:"300 longstreet ",
}
},
As you can see it has so many properties, and to copy them just as we did above by doing something like title = song.title
can be so tiresome.
Here is where the Object.assign()
function comes in.
This function is used places where we want to create a
modified version of an existing object.
const onVote = (id) => {
const newSongs = songs.map((song) => {
if (song.id === id) {
return Object.assign({}, song, {
votes: song.votes + 1,
});
} else {
return song;
}
});
setSongs(newSongs);
};
`
We are passing three arguments to the function. The first argument is a new JavaScript object, the one that Object.assign()
will basically return. The second is the object whose properties we’d like to build off of. The last is the changes we’d like to apply.
So there is no need to assign all those properties one by one to the new object.
Conclusion
This article has clearly explained how to modify and update an object that's in state without mutation. It has also elaborated on the JavaScript function (Object.assign()
) that easily copies an existing JavaScript object into a new one and returns a modified object.
Latest comments (4)
You should look into useReducer. It's an alternative to useState but for more complex objects like what you have here.
Thank you. However I just wanted to keep it basic, but i will consider useReducer too as a Scale up for the article.
Good one but I think spread operator is the best way to update react state in such case
I understand there are a couple of ways to achieve the same, I just wanted to make it basic. Thank You.