React.js manages the components in a tree-like structure to define hierarchy and make it easier for the user to render components together.
The state is the current condition of the data in a component that can be changed while interacting with the application. It helps to improve user state interactivity and explore the dynamic capabilities of components.
React
offers a useState
hook that allows saving state variables and their current state. It declares a two-parameter array containing a state variable and function that is used to change the state.
const [variable , setVariable] = useState([]);
useState
hook can be used in different ways to manage the states in a tree component. Let's take a look at the three main ways in which useState
helps to change the state of the components.
Nested state updation
Event handling
State change propagation
import { useState } from "react" import Button from "./Button"; const AddTask = ({onAdd}) => { const [text , setText] = useState('') const [date , setDate] = useState('') const [time , setTime] = useState('') const onSubmit = (e) => { e.preventDefault() if(!text) { alert("Kindly Fill Text Field") return } onAdd({text , date , time}); setText(''); setDate(''); setTime(''); } const onPress = () => { alert("Your request is recorded"); } return ( <form className = "myform" onSubmit = {onSubmit}> <div> <label>Task: </label> <input type = "text" placeholder="Enter your Task here" value = {text} onChange={(e) => setText(e.target.value)}/> <label>Date: </label> <input type = "date" placeholder="Enter the Date" value = {date} onChange={(e) => setDate(e.target.value)}/> <label>Time: </label> <input type = "time" placeholder="Enter the Time" value = {time} onChange={(e) => setTime(e.target.value)}/> <div className = 'submit'> <Button text = "Submit" action = {() => onPress()}/> </div> </div> </form> ) } export default AddTask
Note: Click here to learn CRUD operations implementation through this example.
Nested state updation refers to updating data within a nested state object while preserving its immutability. In this example, we have declared a state variable tasks
that contains state objects having id
, text
, date
, and time
properties. setTasks
function is used to update the state object properties.
const [tasks , setTasks] = useState([{id: 1 ,text: "Task 1" ,date: "2023-07-12" ,time: "2 : 30pm"},{id: 2 ,text: "Task 2" ,date: "2023-07-20" ,time: "8 : 30pm"}]);
An onUpdate
function is passed to Task.js
, the child component, by App.js
, the parent component. This function uses a map
function to find the state object that is to be updated. This is done by iterating the tasks
array and comparing the sent id
with each state object's id
. Notice that a mirror state object is created with the updated text
property and then the state function is called to set the updated state of that object.
const onUpdate = (id, newtxt) => {const updatedTasks = tasks.map((task) => {if (task.id === id) {return {...task, text: newtxt};}return task;});setTasks(updatedTasks);}
Event handling is a response to an external input given by the user e.g. clicking a button. In this example, when the "remove item" button is clicked in Task.js
, child component, the external input is given to the application that triggers the Remove
function call.
<Task tasks = {tasks} onDelete = {Remove} onUpdate = {onUpdate} />
The Remove
function gets the trigger request in App.js
, the parent component. A filter
is used to set the state of all the state objects except for the one which has id
that is passed as a parameter.
const Remove = (id) => {setTasks(tasks.filter((task) => task.id !== id))}
State change propagation is when a state variable is passed as a parameter to the child component by the parent component. In this example, tasks
state variable containing different state objects is sent to the Task.js
. In this case, App.js
is the parent component and Task.js
is the child component.
<Task tasks = {tasks} onDelete = {Remove} onUpdate = {onUpdate} />
Task.js
, child component of App.js
, can further send all the state objects from tasks
to OneTask.js
, its child component. The state of all the objects is preserved during this and the state change requests are handled in App.js
, the parent component. This is the modified code for the above-mentioned approach.
const Task = ({tasks , onDelete , onUpdate}) => {return(<>{tasks.map((mytask) =>(<OneTask key = {mytask.id} task = {mytask} onDelete = {onDelete} onUpdate = {onUpdate}/>))}</>)}
State management types | How it works |
Nested state updation | The state object is updated while keeping the immutability preserved. A mirror image of the state object is made with the updated properties. |
Event handling | It is triggered when an external input is given and changes the state of the objects according to the requirement. |
State change propagation | Propagation means to send out. Therefore, a state variable is sent as a parameter to the child component by the parent component. |
<label>Date: </label>
<input type = "date" placeholder="Enter the Date" value = {date} onChange={(e) => setDate(e.target.value)}/>
What type of state management is happening here?
Nested state updation
Event handling
State change propagation