By Dmitri Pavlutin

Translator: Front-end small wisdom

Source: dmitripavlutin.com

The more you know, the more you don’t know

Like and watch. Make it a habit


GitHub: github.com/qq449245884… Has been included, more past the classification of the highly praised articles, also collated a lot of my documentation, and tutorial materials. Welcome to Star and perfect, you can refer to the interview test point review, I hope we have something together.

State is information hidden in a component that can modify its state without the parent knowing. I prefer function components because they are simple enough that to give function components state management, you can useState() hooks.

This article will show you step by step how to use the useState() Hook. In addition, some common useState() pits are described.

1. UseuseState()Perform state management

Stateless function components have no state, as shown (part of the code) :

import React from 'react';

function Bulbs() {
  return <div className="bulb-off" />;
}
Copy the code

Try it out at Codesandbox.

Operation effect:

How do you add a button to turn the light bulb on/off? To do this, we need a function component that has a state, that is, a state function component.

UseState () is the Hoook that implements the light bulb switch state, and adding the state to the function component requires four steps: enable the state, initialize, read, and update.

1.1 Enable Status

To convert

to stateful components, you need to tell React to import the useState hook from the ‘React’ package and then call useState() at the top of the component function.

It looks something like this:

import React, { useState } from 'react'; function Bulbs() { ... = useState(...) ; return <div className="bulb-off" />; }Copy the code

Call useState() on the first line of the Bulbs function (don’t check the parameters and return value of the Hook for now). Importantly, calling a Hook inside a component makes that function a stateful function component.

With the state enabled, the next step is to initialize it.

1.2 Initialization Status

To start, the bulb is off, and the corresponding state should use false to initialize the Hook:

import React, { useState } from 'react';

function Bulbs() {
  ... = useState(false);
  return <div className="bulb-off" />;
}
Copy the code

UseState (false) Initializes the state with false.

Once the state is enabled and initialized, how do you read it? Let’s see what useState(false) returns.

1.3 Read Status

When hook useState(initialState) is called, it returns an array whose first item is the status value

const stateArray = useState(false); stateArray[0]; // => Status valueCopy the code

Let’s read the state of the component

function Bulbs() {
  const stateArray = useState(false);
  return <div className={stateArray[0] ? 'bulb-on' : 'bulb-off'} />;
}
Copy the code

The

component status is initialized to false. You can open Codesandbox to see the effect.

UseState (false) returns an array with the first item containing the status value, which is currently false(because the state was initialized with false).

We can use array deconstruction to extract the state value to the variable on:

import React, { useState } from 'react';

function Bulbs() {
  const [on] = useState(false);
  return <div className={on ? 'bulb-on' : 'bulb-off'} />;
}
Copy the code

The on state variable stores the state value.

The state has been enabled and initialized and can now be read. But how to update it? Let’s see what useState(initialState) returns.

####1.4 Status update

Update status with values

As we already know, useState(initialState) returns an array where the first item is the state value and the second is a function to update the state.

const [state, setState] = useState(initialState); // Change the state to 'newState' and trigger a rerendering of setState(newState); // Rerender 'state' to 'newState'Copy the code

To update the state of a component, call the updater function setState(newState) with the newState. After the component is rerendered, the state receives a new value, newState.

Changes the light bulb switch status to true when clicking the light on button and false when clicking the light off button.

import React, { useState } from 'react'; function Bulbs() { const [on, setOn] = useState(false); const lightOn = () => setOn(true); const lightOff = () => setOn(false); return ( <> <div className={on ? </button> <button onClick={lightOff}> </button> </button> </button> </>); }Copy the code

Open codesandbox and try it yourself.

When the light button is clicked, the lightOn() function updates on to true: setOn(true). The same thing happens when you click to turn off the light, except the status is updated to false.

Once the state changes, React rerenders the component and the on variable gets the new state value.

Status updates are a response to events that provide some new information. These events include button clicks, HTTP request completion, and so on, ensuring that the status update function is called in event callbacks or other Hook callbacks.

Use callbacks to update the status

When calculating a new state using a previous state, you can update the state using a callback:

const [state, setState] = useState(initialState); . setState(prevState => nextState); .Copy the code

Here are some examples:

// Toggle a boolean const [toggled, setToggled] = useState(false); setToggled(toggled => ! toggled); // Increase a counter const [count, setCount] = useState(0); setCount(count => count + 1); // Add an item to array const [items, setItems] = useState([]); setItems(items => [...items, 'New Item']);Copy the code

Next, the above light example is reimplemented in this way:

import React, { useState } from 'react'; function Bulbs() { const [on, setOn] = useState(false); const lightSwitch = () => setOn(on => ! on); return ( <> <div className={on ? 'the bulbs - on' : 'bulbs - off'} / > < button onClick = {lightSwitch} > turn on the light/to turn off the lights < / button > < / a >). }Copy the code

Open codesandbox and try it yourself.

setOn(on => ! On) uses the function to update the state.

1.5 Summary a wave

  • Call the useState() Hook to enable state in the function component.

  • The first argument of useState(initialValue), initialValue, is the initialValue of the state.

  • [state, setState] = useState(initialValue) Returns an array of two elements: the state value and the state update function.

  • The state is updated by calling the state updater function setState(newState) with the new value. Alternatively, the state updater can be called with a callback setState(prev => next) that returns a new state based on the previous state.

  • After calling the status updater, React ensures that the component is rerendered so that the new state becomes the current state.

2. Multiple states

A function component can have multiple states by calling useState() multiple times.

function MyComponent() {
  const [state1, setState1] = useState(initial1);
  const [state2, setState2] = useState(initial2);
  const [state3, setState3] = useState(initial3);
  // ...
}
Copy the code

It’s important to make sure that multiple calls to useState() are always in the same order between renderings (more on that later).

We add a button to add a light bulb, and a new state to save the number of light bulbs. When clicked, a new light bulb will be added.

The new state count contains the number of bulbs, with an initial value of 1:

import React, { useState } from 'react'; function Bulbs() { const [on, setOn] = useState(false); const [count, setCount] = useState(1); const lightSwitch = () => setOn(on => ! on); const addBulbs = () => setCount(count => count + 1); const bulb = <div className={on ? 'bulb-on' : 'bulb-off'} />; const bulbs = Array(count).fill(bulb); Return (<> <div className="bulbs">{bulbs}</div> <button onClick={lightSwitch}> On/off </button> <button OnClick ={addBulbs}> addBulbs </button> </>); }Copy the code

Open the demo, then click the Add Light Bulb button: as the number of light bulbs increases, click the On/Off button to turn the light bulb on/off.

  • [on, setOn] = useState(false) Manages the on/off status
  • [count, setCount] = useState(1)Manage the number of bulbs.

Multiple states can work correctly in a component.

3. Delayed initialization of the state

UseState (initialState) is executed every time React rerenders a component. If the initial state is the original value (number, Boolean, etc.), there is no performance problem.

When the initial state requires expensive performance operations, you can use the delayed initialization of the state by providing a function for useState(computeInitialState), as shown below:

function MyComponent({ bigJsonData }) {
  const [value, setValue] = useState(function getInitialState() {
    const object = JSON.parse(bigJsonData); // expensive operation
    return object.initialValue;
  });

  // ...
}
Copy the code

GetInitialState () is executed only once during initial rendering to get the initial state. In future component renderings, getInitialState() is not called again, thus skipping the expensive operation.

4. The pit in useState()

Now that we have a basic understanding of how to use useState(), however, we must be aware of the common problems we may encounter when using useState().

4.1 Where is the calluseState()

When using the useState() Hook, you must follow the rules of the Hook

  1. Call Hook only at the top level: useState() cannot be called in loops, conditions, nested functions, etc. In multiple useState() calls, the order of calls between renderings must be the same.

  2. Call hooks only from React functions: you must call useState() only inside function components or custom hooks.

Let’s take a look at some examples of how useState() can be used correctly and incorrectly.

Effective calluseState()

UseState () is called correctly at the top level of the function component

function Bulbs() {
  // Good
  const [on, setOn] = useState(false);
  // ...
}
Copy the code

Call multiple useState() calls correctly in the same order:

function Bulbs() {
  // Good
  const [on, setOn] = useState(false);
  const [count, setCount] = useState(1);
  // ...
Copy the code

UseState () is called correctly at the top level of the custom hook

function toggleHook(initial) {
  // Good
  const [on, setOn] = useState(initial);
  return [on, () => setOn(!on)];
}

function Bulbs() {
  const [on, toggle] = toggleHook(false);
  // ...
}
Copy the code

useState()Invalid call to

It is incorrect to call useState() in a condition:

function Switch({ isSwitchEnabled }) {
  if (isSwitchEnabled) {
    // Bad
    const [on, setOn] = useState(false);
  }
  // ...
}
Copy the code

It is also wrong to call useState() in a nested function

function Switch() {
  let on = false;
  let setOn = () => {};

  function enableSwitch() {
    // Bad
    [on, setOn] = useState(false);
  }

  return (
    <button onClick={enableSwitch}>
      Enable light switch state
    </button>
  );
}
Copy the code

4.2 Outdated Status

A closure is a function that captures variables from an external scope.

Closures (such as event handlers, callbacks) may capture state variables from the function component scope. Because state variables change between renderings, closures should capture variables with the latest state value. Otherwise, if the closure captures out-of-date state values, you may run into out-of-date state problems.

Let’s see how an outdated state manifests itself. Component

Delays the number of button clicks by 3 seconds.

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

  const handleClickAsync = () => {
    setTimeout(function delay() {
      setCount(count + 1);
    }, 3000);
  }

  return (
    <div>
      {count}
      <button onClick={handleClickAsync}>Increase async</button>
    </div>
  );
}
Copy the code

Open the demo and click the button several times quickly. The count variable does not correctly record the actual number of clicks, and some hits are eaten.

Delay () is an outdated closure that captures the outdated count variable from the initial render (when initialized with 0).

To solve this problem, use the function method to update the count state:

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

  const handleClickAsync = () => {
    setTimeout(function delay() {
      setCount(count => count + 1);
    }, 3000);
  }

  return (
    <div>
      {count}
      <button onClick={handleClickAsync}>Increase async</button>
    </div>
  );
}
Copy the code

Now etCount(count => count + 1) updates the count state correctly in delay(). React ensures that the updated state value is supplied as a parameter to the updated state function. Obsolete closures are resolved.

Open the demo and quickly click the button. Once the delay has passed, count will correctly represent the number of clicks.

4.3 Complex State Management

UseState () is used to manage simple states. For complex state management, you can use the useReducer() hook. It provides better support for states that require multiple state operations.

Suppose you need to write a list of favorite movies. Users can add movies and delete existing movies in the following way:

import React, { useState } from 'react'; function FavoriteMovies() { const [movies, setMovies] = useState([{ name: 'Heat' }]); const add = movie => setMovies([...movies, movie]); const remove = index => { setMovies([ ...movies.slice(0, index), ...movies.slice(index + 1) ]); } return ( // Use add(movie) and remove(index)... ) ; }Copy the code

Try demo: Add and delete your favorite movie.

State lists require several actions: adding and removing movies, and state management details clutter up components.

A better solution is to extract the complex state management into the reducer:

import React, { useReducer } from 'react'; function reducer(state, action) { switch (action.type) { case 'add': return [...state, action.item]; case 'remove': return [ ...state.slice(0, action.index), ...state.slice(action.index + 1) ]; default: throw new Error(); } } function FavoriteMovies() { const [state, dispatch] = useReducer(reducer, [{ name: 'Heat' }]); return ( // Use dispatch({ type: 'add', item: movie }) // and dispatch({ type: 'remove', index })... ) ; }Copy the code

There are two types of operations on the reducer to manage movie states:

  • “Add” inserts the new movie into the list

  • “Remove” removes movies from the list by index

Try to demonstrate and note that the component functionality has not changed. However, this version of

is easier to understand because the state management has been extracted into the Reducer.

Another benefit: You can extract reducer into a separate module and reuse it in other components. In addition, you can unit test the Reducer even if there are no components.

This is the power of separation of concerns: components render the UI and respond to events, while reducer performs state operations.

4.4 Status vs Reference

Consider a scenario where we want to count the number of times a component is rendered.

An easy way to do this is to initialize the countRender state and update it every time you render (using the useEffect() hook)

import React, { useState, useEffect } from 'react';

function CountMyRenders() {
  const [countRender, setCountRender] = useState(0);
  
  useEffect(function afterRender() {
    setCountRender(countRender => countRender + 1);
  });

  return (
    <div>I've rendered {countRender} times</div>
  );
}
Copy the code

UseEffect () calls the afterRender() callback after each render. But once the countRender status is updated, the component is rerendered. This triggers another status update and another re-render, and so on.

The mutable reference useRef() holds mutable data that will not trigger rerendering if changed. Use the mutable reference to modify < countMylink > :

import React, { useRef, useEffect } from 'react';

function CountMyRenders() {
  const countRenderRef = useRef(1);
  
  useEffect(function afterRender() {
    countRenderRef.current++;
  });

  return (
    <div>I've rendered {countRenderRef.current} times</div>
  );
}
Copy the code

Open the demo and click the button a few times to trigger a re-render.

The value of the countRenderRef variable reference increments countrenderref.current ++ each time the component is rendered. Importantly, the change does not trigger a rerendering of the component.

5. To summarize

To make a function component stateful, call useState() in the function body of the component.

The first parameter of useState(initialState) is the initialState. The returned array has two items: the current state and the status update function.

const [state, setState] = useState(initialState);
Copy the code

Use setState(newState) to update the status value. In addition, if you need to update the state based on the previous state, you can use the callback setState(prevState => newState).

You can have multiple states in a single component: useState() is called multiple times.

Delayed initialization is convenient when the initial state is expensive. UseState (computeInitialState) is called with a callback that computeInitialState, and this callback is executed only once during the initial rendering.

You must ensure that using useState() follows the Hook rules.

The issue of out-of-date state arises when closures capture out-of-date state variables. You can solve this problem by updating the state with a callback that calculates the new state based on the previous state.

Finally, you’ll use useState() to manage a simple state. To handle more complex states, a better option is to use the useReducer() hook.


Original text: dmitripavlutin.com/react-usest…

The possible bugs after the deployment of code can not be known in real time, in order to solve these bugs, spent a lot of time log debugging, here by the way to recommend a good BUG monitoring tool Fundebug.


Communication (welcome to join the group, the group will give red envelopes on weekdays, interactive discussion technology)

Dry goods series of articles summarized as follows, feel good point a Star, welcome to add group to learn from each other.

Github.com/qq449245884…

Due to space restrictions, today’s share will only go here. If you want to know more content, you can scan the QR code at the bottom of each article, and then follow our wechat public account, to learn more information and valuable content.