How to manage the state in a tree component in React.js

React.js manages the components in a tree-like structure to define hierarchy and make it easier for the user to render components together.

Tree structure of components in React.js.
Tree structure of components in React.js.

What is a state in React.js?

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([]);

How to manage a state

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.

  1. Nested state updation

  2. Event handling

  3. State change propagation

Example

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
A simple to-do list application.

Note: Click here to learn CRUD operations implementation through this example.

Nested state updation in the 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 in the example

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 in the example

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}/>
))}
</>
)
}

Summary

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.

Test your understanding

Q
<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?

A)

Nested state updation

B)

Event handling

C)

State change propagation

Copyright ©2024 Educative, Inc. All rights reserved