React States, Passing Data and Lists, Event Handling

Syed Aquib AliSyed Aquib Ali
6 min read

React States

In React, state is a built-in object that allows components to create and manage their own data. State is used to keep track of information that can change over time and influence what is rendered on the screen.

Using State

The useState hook is used to manage state. The useState hook returns an array with two elements: the current state value and a function to update it.

import React, { useState } from 'react';

function App() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

State Characteristics

  1. Initialization:

    • State can be initialized with a default value as an argument to useState.
  2. Updating State:

    • State updates are asynchronous, and React may batch multiple setState calls for performance reasons.

    • The state should not be directly mutated. Always use setState (class components) or the state updater function (functional components) to update the state.

  3. Conditional Rendering:

    • The state can be used to conditionally render parts of the UI.
function App() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const toggleLogin = () => {
    setIsLoggedIn(!isLoggedIn);
  }

  return (
    <div>
      {isLoggedIn ? <p>Welcome back!</p> : <p>Please log in.</p>}
      <button onClick={toggleLogin}>
        {isLoggedIn ? 'Log out' : 'Log in'}
      </button>
    </div>
  );
}
  • By default the value is set to false, hence it will display as Please log in. and after clicking it, it will say Welcome back on the page.

Handling Multiple State Variables

You can use multiple useState hooks to manage different state variables in a functional component.

function App() {
  const [name, setName] = useState("");
  const [age, setAge] = useState(0);

  const handleNameChange = (event) => setName(event.target.value);
  const handleAgeChange = (event) => setAge(Number(event.target.value));

  return (
    <div>
      <input type="text" value={name} onChange={handleNameChange} placeholder="Name" />
      <input type="number" value={age} onChange={handleAgeChange} placeholder="Age" />
      <p>Name: {name}, Age: {age}</p>
    </div>
  );
}

Using State with Objects

If your state is an object, updating it requires spreading the previous state to ensure all properties are preserved.

function App() {
  const [user, setUser] = useState({ name: "", age: 0 });

  const handleNameChange = (event) => setUser({ ...user, name: event.target.value });
  const handleAgeChange = (event) => setUser({ ...user, age: Number(event.target.value) });

  return (
    <div>
      <input type="text" value={user.name} onChange={handleNameChange} placeholder="Name" />
      <input type="number" value={user.age} onChange={handleAgeChange} placeholder="Age" />
      <p>Name: {user.name}, Age: {user.age}</p>
    </div>
  );
}

State is a fundamental concept in React, enabling components to maintain dynamic and interactive UIs.


Passing Data

There are cases when you want to pass the data from parent to child. Let's understand this while creating a list app and how we can pass the data to child components and use it. Here's how we can do it:

Suppose we have a component calledNote.jswhich is connected to ourApp.js:

import React, { useState } from 'react';
// Child component
import SingleNote from './SingleNote';

function Note() {

    const [notes, setNotes] = useState([]);
    const [curNotes, setCurNotes] = useState("");

    function updateCurNote(event) {
      setCurNotes(event.target.value);
    }

    function addNotes() {
      const newNote = [...notes, curNotes];
      setNotes(newNote);
    }

  return (
    <>
      <input type="text" onChange={updateCurNote} />
      <button onClick={addNotes}>Submit</button>
      <ul>
        {notes.map((note, i) => { 
                              {/* Sending the data to child */}
          return (<li key={i}><SingleNote note={note}/></li>)
        })}
      </ul>
    </>
  );
}

export default Note;
  1. State Management:

    • notes: An array to store all the notes.

    • curNotes: A string to store the current input note.

  2. Handlers:

    • updateCurNote(event): Updates the curNotes state whenever the user types into the input field.

    • addNotes(): Adds the current note (stored in curNotes) to the notes array and updates the notes state.

  3. Rendering:

    • An input field to capture the user's note, with an onChange event that triggers updateCurNote.

    • A submit button that, when clicked, calls addNotes to add the current note to the list.

    • A list (ul) that displays all notes. Each note is rendered as a list item (li) and passed as a prop to the SingleNote child component.

Child component:

import React from 'react';

                  // Destructuring to directly access notes. 
function SingleNote({note}) {
  return (
    <>
        {note}
    </>
  )
}

export default SingleNote;

Else we had to writepropsinside the parenthesis and inside returnprops.note, like this:

import React from 'react';
function SingleNote(props) {
  return (
    <>
        {props.note}
    </>
  )
}

export default SingleNote;

Passing Data From Child To Parent

We understood how we can pass the data from parent to child, but what if we wanted to pass the child data to parent instead? There can be many use cases related to this.

Let's create a counting app again and understand how we can create such a flow:

// Parent.js

import React, { useState } from 'react'
import Child1 from './Child1';
import Child2 from './Child2';

function Parent() {

    const [countInParent, setCountInParent] = useState(0);

    function updateCountInParent(count) {
        setCountInParent(count);
    }

  return (
    <>
        <p>Inside parent: {countInParent}</p>

                           {/* A callback function from where we will take and store our data */}
        <Child1 onCountUpdate={updateCountInParent}/>

                             {/* Passing the current value to the sibling of child1 */}
        <Child2 countFromParent={countInParent}/>
    </>
  );
}

export default Parent;
  1. State Management:

    • countInParent: A state variable in the Parent component to store a count value, initialized to 0.
  2. Handlers:

    • updateCountInParent(count): A function that updates the countInParent state with a new count value passed as an argument.
  3. Rendering:

    • A paragraph (p) that displays the current value of countInParent.

    • The Child1 component, which is passed a prop onCountUpdate that references the updateCountInParent function. This allows Child1 to update the count in the parent component.

    • The Child2 component, which is passed a prop countFromParent that holds the current value of countInParent. This allows Child2 to access and use the count value from the parent component.

// child1.js

import React, { useState } from 'react'

function Child1(props) {

  const [countInChild1, setCountInChild1] = useState(0);

  function updateCountInChild1() {
    setCountInChild1(countInChild1 + 1);

    // Sending back the data to parent in callback
    props.onCountUpdate(countInChild1 + 1);
  }

  return (
    <>
      <p>Inside child 1: {countInChild1}</p>
      <button onClick={updateCountInChild1}>increment</button>
    </>
  );
}

export default Child1;
  1. State Management:

    • countInChild1: A state variable in the Child1 component to store a count value, initialized to 0.
  2. Handlers:

    • updateCountInChild1(): A function that increments the countInChild1 state by 1 and then calls the onCountUpdate prop function (passed from the parent) with the updated count value (countInChild1 + 1).
  3. Rendering:

    • A paragraph (p) that displays the current value of countInChild1.

    • A button that, when clicked, calls updateCountInChild1 to increment the count and notify the parent component of the new count.

// child2.js

import React from 'react'

function Child2(props) {
  return (
    <>
      <p>Inside child 2: {props.countFromParent}</p>
    </>
  )
}

export default Child2;

This setup allows Child1 to send a count update to the parent component, which then updates its state and passes the updated count to Child2, ensuring that both child components can interact with and reflect changes in the parent component's state.


Event Handling

So far we have already seen few Event Handling's, such as onclick: When a user clicks in that area a certain function will be called. Such as that React supports a wide range of event types:

  1. Mouse Events: onClick, onDoubleClick, onMouseEnter, onMouseLeave

  2. Keyboard Events: onKeyDown, onKeyPress, onKeyUp

  3. Form Events: onChange, onSubmit

  4. Focus Events: onFocus, onBlur

  5. Clipboard Events: onCopy, onCut, onPaste

Event handling in React allows you to create interactive UIs by responding to user actions and updating the state or performing other actions based on those events.

0
Subscribe to my newsletter

Read articles from Syed Aquib Ali directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Syed Aquib Ali
Syed Aquib Ali

I am a MERN stack developer who has learnt everything yet trying to polish his skills 🥂, I love backend more than frontend.