Http://medium.com/ryardley/r… Translation address: github.com/xiao-T/note… The copyright of this article belongs to the original author. Translation is for study only.

Use diagrams to uncover the rules of use in the specification

I’m a fan of the new API Hooks. However, there are some strange constraints to using it. Here, I present a model for those of you who want to use the new API, but don’t understand the rules, to better understand the rules.

Learn how hooks work

I’ve heard that some people are having trouble understanding the specification for those hooks APIS, so, on the surface, I can try to reveal how the new syntax in the specification works.

The rules of the Hooks

There are two usage rules that the React core team explicitly prescribes. When using hooks, you should follow the documentation.

  • Do not call Hooks inside loops, conditional statements, and nested methods
  • Call Hooks only in function components

The second goes without saying. In order for functional components to be more powerful, you need methods to implement them.

However, for the first one, there is a bit of confusion, which is a bit unconventional. That’s what I want to explain.

Hooks manage State with an array

To get a clear and unambiguous model, let’s implement a simple version of the Hooks API.

Please note: this is just a guess, to demonstrate how I understand Hooks, the only way to implement them. This is not really how Hooks are implemented internally.

How to implementuseState()?

Let’s go through a demonstration to explain how the hook works.

First, start with a component:

function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi");
  const [lastName, setLastName] = useState("Yardley");

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

The idea behind the Hooks API is that you can return a setter method from a hook function that then controls the state returned by the hook.

How does React handle this?

Let’s explain how React works inside. The following will be executed in the context of the specific component. That is, the data is stored one layer outside the component after it has been rendered. This data is not shared with other components, but can be accessed by specific components in subsequent renderings.

1) Initialization

Create two empty arrays: setters and state

Set the pointer to 0

2) First render

Execute the function to perform the first rendering of the component.

For the first rendering, each time useState() is executed, add a setter method (binding pointer position) to the setters array, and then add the corresponding state to the setate array.

3) Subsequent rendering

Each subsequent render pointer is reset and the corresponding data is read.

4) Event handling

Each setter has a reference to the position of the pointer, so any call to the setter triggers a change in the position data in the state array.

The last simple version of the implementation

The following code just shows how.

Note: This does not mean that all hooks are implemented this way, but it should give you a good idea of how hooks work in components. That is why we are using module level vars etc.

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

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

// 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];
}

// 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
Copy the code

Why is order important

Now, what happens if we change the order of the hooks in the component based on some external data or component state?

Let’s do something the React team isn’t allowed to do:

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>
  );
}
Copy the code

There is a useState in the conditional statement above. So let’s see what this does to the system.

The first rendering of the bad component

At this point, our variables firstName and lastName both have correct data, but let’s see what happens on the second rendering.

Secondary rendering of bad components

Now, firstName and lastName are both set to “Rudi”, which makes the state inconsistent. It was clearly a mistake and it didn’t work. However, it helps to understand why React Hooks have those rules.

The React team uses rules explicitly because they can lead to data inconsistencies if they are not followed

Imagine that hooks are maintained through arrays and you won’t break the rule

It should now be clear why hooks cannot be called in conditional statements or loops. Because we maintain the correspondence between setter and state through array Pointers, if we change the call order during rendering, the Pointers will not match setter and state correctly.

So here’s what we need to care about: think of the Hooks that manage the business as a fixed array of Pointers with no change. If you do, everything will be fine.

conclusion

Hopefully I’ve put together a clearer model of how the new Hooks API works underneath. Remember that real values are associative arrays, so pay more attention to the order between them, and use the hooks API to your advantage.

Hooks are valid apis for the React component. People like it for a reason, and if you’re going to store state in this kind of model, then you don’t want to violate the usage rules.