DEV Community

Daniel.xiao
Daniel.xiao

Posted on

React Hooks - How to use state safely

Hi, Daniel, let's talk about "How to use state safely" today?

Daniel: Safe? Is it dangerous to use the state of React Hooks? X_X I am so nervous now~

Take it easy, man. About safety, I will give you an example. Without the example, it is difficult to explain it clearly.

Daniel: Ok. Does anyone reading this article know about React Hooks?

Maybe some one don't know much about it, so I will briefly introduce it:

React Hooks is a good thing, it allows you to implement stateful components with pure functions, so you don't have to worry about whether the component is stateful or stateless, no need to hesitate between the pure function and the class implementation (of course, the benefits are many, not only this. I will write an article about "Why React Hooks" later.)

Once you are on the way of React Hooks, you will encounter a variety of strange problems.

This is normal, the birth of a new thing, always accompanied by various problems, and then constantly upgraded and grew up in the fight, and eventually became. . .

Daniel: Hey, don’t pull away.

🤪 If you have read the official documentation on React Hooks and have seen some examples inside, you may think that it is quite simple, it is just changed to use state with useState, no difficulty.

However, just in the moment you relax your vigilance, "danger" is quietly coming down in some corners.

Daniel: Where is the danger? Where ? Where ? Where ?

😅 ~~

For the state value to get, you may have got an old value that is not what you expected, that is, not the latest state value.

You need to stay awake all the time to make it possible to get around these "dangers."

Daniel: 😵

You look confused., I will give an example now.

Example is coming: When you fill out some information on the form and then leave the form, you want to automatically save it as a draft, so you can restore it the next time you come in.

The implementation of the simplified version is given below:

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

export const UseStateDemoComp1 = () => {
  const [name, setName] = useState('daniel');

  useEffect(function() {
    return () => {
      saveDraft()
    }
  }, [])

  function saveDraft() {
    console.log('Save Draft:', name);
  }

  return (
    <div>
      <form>
        <input value={name} onChange={e => setName(e.target.value)}></input>
      </form>
    </div>
  );
};

Enter fullscreen mode Exit fullscreen mode

The code seems to have no problem.

The function passed to useEffect returns a function that is equivalent to the original componentWillUnmount lifecycle method, which is called only when the component is destroyed. We can execute some logic in the method, here call the saveDraft method, get the value of the name state, and save.

Did you find the problem? If not, then let's take a look at the picture and see what the problem is:

Demonstration

Clicking the Toggle button will destroy the component, so the destroy action will be triggered. As you can see from the gif, we filled in "sarah", but in the method of destroying execution, the value obtained is "daniel".

Daniel: Why is this? Shouldn't it be the latest value? 😶

Because the dependency of useEffect is an empty array, it will only be executed once during the entire component life cycle, that is, after the component finishes rendering for the first time, and the state value used in the useEffect method is the latest state value at that time. Can be understood with a snapshot. In the next time, no matter how many times the component is re-rendered, it will not change the value of the state inside, because it is just the snapshot at that time.

Some one may say that we can fix it by adding name to the array of useEffect depends on it. as follows:

useEffect(function() {
    return () => {
      saveDraft()
    }
}, [name])
Enter fullscreen mode Exit fullscreen mode

It seems to satisfy the needs, but there are still problems. Because I only want to save when the component exits, but the result is that once the form field value changes, it will be saved, and the save operation becomes very frequent.

Demonstration

Daniel: So you want to say that React Hooks has nothing to do with this?

Of course not, the above requirements can be achieved by useRef and useEffect. How to achieve it? Try it yourself. When you implement it, you will find that the code is not only lengthy, but also poorly readable.

Daniel: Ok, start your show now.

With the above example, here is a description of safely using state:

"Safely using state is that no matter when and where you read the value of state, it always meets your expectations, always the latest value, without you being careful to judge whether it will be an old value that has not been updated"

The official provided custom hooks capabilities, it want to continue to improve Hooks through the efforts of the community.

Next, we will use [nice-hooks], the third-party custom Hooks open source project, using its useSingleState instead of useState, here is the example:

import React, { useEffect } from "react";
import { useSingleState } from "nice-hooks";

export const DemoComp1 = () => {
  const [state, setState] = useSingleState({
    name: 'daniel'
  });

  useEffect(function() {
    return () => {
      saveDraft()
    }
  }, [])

  function saveDraft() {
    console.log('Save Draft:', state.name);
  }

  return (
    <div>
      <form>
        <input value={state.name} onChange={e => setState({name: e.target.value})}></input>
      </form>
    </div>
  );
};

Enter fullscreen mode Exit fullscreen mode

Let's take a look at the effect directly. Perfect~

Demonstration

Here is a hook about useSingleState: it uses state like this.state and this.setState in the form of class, so it's very easy to get started. The most important thing is that it can safely use state and have the ability to callback.

Finally, we use the hook useLifeCycle to improve the code. Now, the code below is a lot better than using the official hooks directly.

import React from "react";
import { useSingleState, useLifeCycle } from "nice-hooks";

export const DemoComp1 = () => {
  const [state, setState] = useSingleState({
    name: 'daniel'
  });

  useLifeCycle({
    willUnmount() {
      saveDraft()
    }
  })

  function saveDraft() {
    console.log('Save Draft:', state.name);
  }

  return (
    <div>
      <form>
        <input value={state.name} onChange={e => setState({name: e.target.value})}></input>
      </form>
    </div>
  );
};

Enter fullscreen mode Exit fullscreen mode

Time is really fast, and it’s time to say goodbye.

If you feel that this article is ok, please give it a heart or clap.

If you think nice-hooks is helpful, please give it a ☆.

ByeBye!


Keywords: react, hooks, nice-hooks

Top comments (0)