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.