React hooks: it's not magic, it's just an array - using diagrams to demystify the rules of the proposal

Original address: https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e

Translation: Dye Mo ( Github )

Translation address: https://github.com/answershuto/Blog

Reprint please famous source

I'm a big fan of the hooks API, however it has some weird constraints on your usage , so I'm using a model in this article to show the rationale for those who want to use the new API but have trouble understanding its rules people.

Warning: Hooks are still experimental

The Hooks API mentioned in this article is still in the experimental stage, if you need the stable React API documentation, you can find it here .

Deciphering how Hooks work

I found some of my classmates struggling to figure out the "magic" in the new Hooks API, so I'm going to try to explain, at least superficially, how it works.

Rules for Hooks

In the Hooks proposal , the React core team proposed two main rules that you must follow when using Hooks.

  • Please do not call Hooks in loops, conditions or nested functions
  • Hooks are called only in React functions

The latter I think is obvious, you need to associate the behavior with the component in a functional way to add the behavior to the component.

For the former, however, I think it's confusing because it doesn't seem natural to program with the API, but that's what I'm going to lasso today.

The state management of Hooks relies on arrays

To give you a clearer model, let's take a look at what a simple implementation of Hooks might look like.

It should be noted that this part of the content is only one possible implementation of the API, so that the reader can better understand it. It's not how the API actually works internally, and it's just a proposal that may change in the future.

How should we implement "useState()"?

Let's understand how state might work with an example.

First let's start with a component:

code address

/* 译:https://github.com/answershuto */
function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi");
  const [lastName, setLastName] = useState("Yardley");

  return (
    <Button onClick={() => setFirstName("Fred")}>Fred</Button>
  );
}

The idea behind the Hooks API is that you can return a setter function through the second parameter of the Hook function, and use this function to control the body of the Hook management.

So what can React do with this?

First let's explain how it works inside React. The following steps are performed when the execution context renders a particular component. This means that data storage is independent of components. This state cannot be shared with other components, but it has a separate scope that reads data when it needs to be rendered.

(1) Initialization

Create two empty arrays "setters" and "state"

Set the pointer "cursor" to 0

(2) First rendering

Execute the component function for the first time

Whenever useState() is called, if it is the first render, it will push a setter method (bound to the pointer "cursor" position) into the setters array, and at the same time, it will also put another corresponding state into the state array.

(3) Subsequent rendering

Each subsequent render resets the position of the pointer "cursor" and reads the corresponding value from each array.

(4) Handling events

Each setter will have a reference to the corresponding pointer location, so when any setter call is triggered it will trigger to change the corresponding value in the state array.

and the underlying implementation

Here is a sample code:

code address

let state = [];
let setters = [];
let firstRun = true;
let cursor = 0;

function createSetter(cursor) {
  return function setterWithCursor(newVal) {
    state[cursor] = newVal;
  };
}

/* 译:https://github.com/answershuto */
// This is the pseudocode for the useState helper
export function useState(initVal) {
  if (firstRun) {
    state.push(initVal);
    setters.push(createSetter(cursor));
    firstRun = false;
  }

  const setter = setters[cursor];
  const value = state[cursor];

  cursor++;
  return [value, setter];
}

/* 译:https://github.com/answershuto */
// Our component code that uses hooks
function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi"); // cursor: 0
  const [lastName, setLastName] = useState("Yardley"); // cursor: 1

  return (
    <div>
      <Button onClick={() => setFirstName("Richard")}>Richard</Button>
      <Button onClick={() => setFirstName("Fred")}>Fred</Button>
    </div>
  );
}

// This is sort of simulating Reacts rendering cycle
function MyComponent() {
  cursor = 0; // resetting the cursor
  return <RenderFunctionComponent />; // render
}

console.log(state); // Pre-render: []
MyComponent();
console.log(state); // First-render: ['Rudi', 'Yardley']
MyComponent();
console.log(state); // Subsequent-render: ['Rudi', 'Yardley']

// click the 'Fred' button

console.log(state); // After-click: ['Fred', 'Yardley']

Why does order matter?

What happens if we change the order of Hooks in the render cycle based on some external condition or the state of the component?

Let's do something that the React team is forbidden to do.

code address

let firstRender = true;

function RenderFunctionComponent() {
  let initName;
  
  if(firstRender){
    [initName] = useState("Rudi");
    firstRender = false;
  }
  const [firstName, setFirstName] = useState(initName);
  const [lastName, setLastName] = useState("Yardley");

  return (
    <Button onClick={() => setFirstName("Fred")}>Fred</Button>
  );
}

We called the useState function in the conditional statement, let's see the damage it does to the whole system.

First render of bad components

At this point, our variables firstName and lastName still contain the correct data, let's move on to see what happens on the second render.

bad second render

Now the firstName and lastName variables are all set to "Rudi", which does not match our actual storage state.

The usage of this example is obviously incorrect, but it gives us an idea of ​​why we must use Hooks according to the rules laid down by the React team.

React 团队制定了这个规则,是因为如果不遵循这套规则去使用 Hooks API会导致数据有问题。

Think Hooks maintain arrays of columns, so you shouldn't break these rules

So you should now clear why you shouldn't use Hooks in conditionals or loops. Because we maintain a pointer "cursor" that points to an array, if you change the order of calls inside the render function, the pointer "cursor" will not match the correct data, and your calls will not point to the correct data or handle.

So, one trick is that you need to think of Hooks as a set of arrays that need to be managed by a matching pointer "cursor". If this is done, it will work fine in any way of writing it.

Summarize

I hope that through the above explanation, I have established a clearer mental model about Hooks for you, so that you can think about what the new Hooks API does at the bottom. Keep in mind that its real value is in being able to focus attention together while being careful about its ordering, and using the Hooks API will pay off.

Hooks are a useful plugin for React components, which is why everyone is so excited about it. If you have the mental model I mentioned above in your mind, that this state exists as a set of arrays, then you will find that it does not break its rules in use.

I hope to look into the useEffects useEffects method in the future and try to compare it to React's lifecycle.

This article is an online document, if you want to contribute or have any errors, please contact me.

You can follow me (Rudi Yardley) on Twitter or find me on Github .

Ran Mo translation: https://github.com/answershuto

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324139599&siteId=291194637