How to Use State in React Functional Components

React components can have internal “state”, a set of key-value pairs that belong to the component. When the state changes, React renders the component. Historically, state could only be used in class components. Using square brackets, you can also apply state to functional components.

The traditional approach

React class components have a state property that owns their state. They provide a setState() method you can use to update the state, triggering a new render.

class MyComponent extends React.Component {
 
    state = {value: 1};
 
    this.updateState = () => this.setState({value: (this.state.value + 1)});
 
    render() {
        return (
            <div>
                <p>{this.state.value}p>
                <button onClick={this.updateState}Increment Valuebutton>
            div>
        );
    }
 
}

In this example, the rendered text will always show the number in the component state. Clicking the button will increase the value.

Conversion to functional component

With such a simple component, it would be ideal to rewrite it as a functional component. To do this, you will need to use the useState() hook. Hooks were added in React 16.8; prior to this release, there was no mechanism to add state to functional components.

This is what the above component looks like as a functional component:

import React, {useState} from "react";
 
const MyComponent = () => {
    const [value, setValue] = useState(1);
    return (
        <div>
            <p>{value}p>
            <button onClick={() => setValue((value + 1))}>Increment Valuebutton>
        div>
    );
};

This is shorter and more readable than the class-based original. Unlike the class component, you cannot access a state instance property or setState() method. In place, useState() is called to set the state and get an update function.

Anatomy of the useState() Hook

Hooks are a feature of React that allow you to “hook” functionality to functional components. Since functions are pure and have no instances, functionality initially implemented as React.Component class methods cannot be used directly. Hooks allow you to add these features to components without having to convert them to classes.

The useState() hook defines an individual state property. It returns an array containing two elements: the value of the current state and a function that you can call with a new value to update the state.

In the example, we use array destructuring assignment to unpack array values ​​into clearly named variables. By convention, the setter method must be prefixed with set because it takes the place of setState() class method.

Call useState() declares a state variable, value in our case, which will be “kept” between function calls. That means useState() is guaranteed to return the same value each time you call it in your component. Any other variable value is lost once a function terminates; React keeps state values ​​internally to make sure you get the same one every time your function runs.

Status update

The status update function is just a normal function. It is used in the onClick handler to override the current state value. React’s internal handling of state values ​​ensures that your component will then render. useState() will provide the new value, causing the state to change.

There is a significant difference from the setState() class components: functional status updates replace the state, while setState() does a shallow merge:

const [value, setValue] = useState({foo: "bar", test: {example: "demo"}});
setValue({foo: "foobar"});
// Results in {foo: "foobar"}
 
this.state = {foo: "bar", test: {example: "demo"}};
this.setState({foo: "foobar"});
// Results in {foo: "foobar", test: {example: "demo"}};

Instead of passing a new state value directly, you can also pass a function to state editors. Functions receive the current state as a parameter and should return the new state value. This is useful when working with toggleable values.

const Checkbox = () => {
    const [checked, setChecked] = useState(false);
    const toggle = previous => !previous;
    return <input checked={checked} onClick={() => setChecked(toggle)} />;
};

This helps you reuse toggle logic in multiple places in your component.

Default values

There is one more point to note about useState(). The hook itself accepts a parameter that sets the initial value of the state variable. In the example above, the value will be initialized to 1. When you do not specify a value, undefined is used. This corresponds to the behavior when configuring the state instance property in a class component.

If you pass a function to useState()React will call it and use its return value as the initial state value.

const MyComponent = () => {
    const initialState = () => 1;
    const [value, setValue] = useState(initialState);
};

This technique allows the initialization of the “lazy” state. The function won’t be called until React is ready to set up state.

Using a function also ensures that the value of the initial state is calculated only once. This is important if determining your initial state requires expensive computation – if you pass it directly, the value will be computed each time the component is rendered, compared to once on the first render if you pass a reference to a function.

const MyComponent = () => {
    const doSomethingExpensive = () => {
        // ...
    }
    const [value, setValue] = useState(doSomethingExpensive());
    const [value, setValue] = useState(doSomethingExpensive);
};

The subtle but significant difference between the two useState() calls illustrates the potential performance improvement. The first line would perform the expensive operation on every render call, even though it was redundant because the state was already initialized. This would not happen in the second case.

Using multiple state values

You have multiple choices when using multiple state values ​​in a single functional component. You can fall back to a class-like system, using a single object stored in state:

const MyComponent = () => {
    const [user, setUser] = useState({id: 1, username: "foobar"});
};

You must be sure to call setUser() with updated user object. The propagation syntax is convenient in the same way as class components:

setUser({...user, username: "example"});

This creates a new object with the existing properties of user. It then updates the username property to its new value. It is important that you create a New object, instead of directly mutating the existing object, so that React’s state reconciliation can identify the change.

Alternatively, you can call useState() multiple times to set up unique state variables for each item. This is often the preferred approach for functional components. This can make it easier to update individual status values.

const MyComponent = () => {
    const [userId, setUserId] = useState(1);
    const [username, setUsername] = useState("foobar");
};

Stateful properties now have their own state variables and update functions.

Conclusion

React useState() hook makes functional components more powerful by allowing them to own state. You can set an initial value, access the current value with the assurance that it will persist between renders, and update the state using a specially provided function.

Stateful functional components are often faster to write than their class-based counterparts. Additionally, they can make it more obvious what is going on in your codebase because references to state and setState() are eliminated in favor of clear variable names. At the end of the day, useState() provides flexibility and means you no longer need to convert functional components to class components when you need them.


Source link

Comments are closed.