Original Address
React Hooks examples of teaching
Currently, Hooks should be React the hottest concepts, and before reading this article, I hope you understand the basic usage of Hooks.
When using Hooks, we may have a lot of doubts
- Why do only the outermost Hook function call, do not judge or subroutine in a loop, a conditional call?
- Why useEffect second argument is an empty array, the equivalent of ComponentDidMount, only once?
- How to Hook a custom function that affect the use of its components?
- How to Capture Value property is generated?
- ......
In this article we will not explain Hooks concepts and usage, but will take you from zero to achieve a tiny hooks, know these know why.
useState
-
The easiest useState usage is this:
demo1: https://codesandbox.io/s/v0nqm309q3function Counter() { var [count, setCount] = useState(0); return ( <div> <div>{count}</div> <Button onClick={() => { setCount(count + 1); }}> 点击 </Button> </div> ); }
-
Based on usage useState, we try to achieve own a useState:
demo2: https://codesandbox.io/s/myy5qvoxppfunction useState(initialValue) { var state = initialValue; function setState(newState) { state = newState; render(); } return [state, setState]; }
-
Then we found, click on Button's time, count and will not change, why? We do not store state, each component rendering Counter time, state is a new reset.
Naturally we can think of, the state extracted, exist outside useState.
Demo3: https://codesandbox.io/s/q9wq6w5k3wvar _state; // 把 state 存储在外面 function useState(initialValue) { _state = _state | initialValue; // 如果没有 _state,说明是第一次执行,把 initialValue 复制给它 function setState(newState) { _state = newState; render(); } return [_state, setState]; }
So far, we have achieved a useState can work, at least for now nothing issue.
Next, let's look at how to achieve useEffect.
useEffect
useEffect is another basic Hook, for treatment of side effects, the most simple usage is this:
demo4: https://codesandbox.io/s/93jp55qyp4
useEffect(() => {
console.log(count);
}, [count]);
We know useEffect have the following characteristics:
- There are two parameters callback and an array of dependencies
- If no dependencies exist, then the callback will be executed every time render
- If dependencies exist only if it has changed, callback will be performed
Let's achieve a useEffect
demo5: https://codesandbox.io/s/3kv3zlvzl1
let _deps; // _deps 记录 useEffect 上一次的 依赖
function useEffect(callback, depArray) {
const hasNoDeps = !depArray; // 如果 dependencies 不存在
const hasChangedDeps = _deps
? !depArray.every((el, i) => el === _deps[i]) // 两次的 dependencies 是否完全相等
: true;
/* 如果 dependencies 不存在,或者 dependencies 有变化*/
if (hasNoDeps || hasChangedDeps) {
callback();
_deps = depArray;
}
}
Here, we can achieve a useEffect a job, it does not seem so difficult.
At this point we should be able to answer a question:
Q: Why does the second argument is an empty array, the equivalent componentDidMount
?
A: Because the dependent has not changed, callback will not execute the second.
Not Magic, just Arrays
Until now, we have achieved useState and useEffect can work. But there is a big problem: Talia can be used only once, because there is only one _state and a _deps. such as
const [count, setCount] = useState(0);
const [username, setUsername] = useState('fan');
count and username will always be equal, because they share a _state, and no place to store two values. We need to store multiple _state and _deps.
Such as " React Hooks: not Magic, the Just Arrays " written, we can use an array to solve the problem Hooks reuse.
demo6: https://codesandbox.io/s/50ww35vkzl
The key is the code:
- First when rendering, in order useState, useEffect of the state, deps like stuffed memoizedState array sequentially.
- Update, in order, from the value of the last recorded memoizedState is out.
- If you still do not know, you can see the following chart.
let memoizedState = []; // hooks 存放在这个数组
let cursor = 0; // 当前 memoizedState 下标
function useState(initialValue) {
memoizedState[cursor] = memoizedState[cursor] || initialValue;
const currentCursor = cursor;
function setState(newState) {
memoizedState[currentCursor] = newState;
render();
}
return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1
}
function useEffect(callback, depArray) {
const hasNoDeps = !depArray;
const deps = memoizedState[cursor];
const hasChangedDeps = deps
? !depArray.every((el, i) => el === deps[i])
: true;
if (hasNoDeps || hasChangedDeps) {
callback();
memoizedState[cursor] = depArray;
}
cursor++;
}
We use diagrams to describe the process of memoizedState and the cursor changes.
1. Initialization
2. The initial rendering
3. Event Trigger
4. Re Render
Here, we can achieve an arbitrary useState and useEffect reuse.
Also, you can answer a few questions:
Q: Why do only the outermost call Hook in function? Why Do not judge or subroutine call in the cycle conditions.
A: memoizedState array is defined in order to place the hook data, if the sequence variation hook, memoizedState not perceived.
Q: How customizable Hook affect its function components?
A: share the same memoizedState, share the same sequence.
Q: "Capture Value" characteristic is how to generate?
A: Every time ReRender time, are re-assembly to perform a function, a function of the components has been performed before and will not do anything.
React to achieve real
Although we basically achieved by an array of available Hooks, Hooks understand the principle, but React in implementation there are some differences.
-
React is instead in the form of an array similar to a single list. Next in order in series through all of the hook.
type Hooks = { memoizedState: any, // 指向当前渲染节点 Fiber baseState: any, // 初始化 initialState, 已经每次 dispatch 之后 newState baseUpdate: Update<any> | null,// 当前需要更新的 Update ,每次更新完之后,会赋值上一个 update,方便 react 在渲染错误的边缘,数据回溯 queue: UpdateQueue<any> | null,// UpdateQueue 通过 next: Hook | null, // link 到下一个 hooks,通过 next 串联每一 hooks } type Effect = { tag: HookEffectTag, // effectTag 标记当前 hook 作用在 life-cycles 的哪一个阶段 create: () => mixed, // 初始化 callback destroy: (() => mixed) | null, // 卸载 callback deps: Array<mixed> | null, next: Effect, // 同上 };
-
memoizedState, cursor is where exist? And how each function components one to one?
We know, react will generate a component tree (or Fiber singly linked list), each tree node corresponds to a component, hooks on the data as a component of the information stored on these nodes, along with the components of birth, death together .