Author: JD Retail Zheng Bingyi
foreword
React Hooks
is React
a new feature introduced in 16.8 that allows state
and other React features to be used in function components without having to use class components. Hooks
are a very important concept because they provide a simpler and more understandable React
development experience.
React Hooks
The core source code mainly includes two parts: React
the internal Hook
manager and a series of preset Hook
functions .
First, let's look at the manager React
inside . Hook
This manager is React
an important internal mechanism responsible for managing everything in the component Hook
and making sure they are called in the correct order during component rendering.
Internal Hook Manager
Example:
const Hook = {
queue: [],
current: null,
};
function useState(initialState) {
const state = Hook.current[Hook.queue.length];
if (!state) {
Hook.queue.push({
state: typeof initialState === 'function' ? initialState() : initialState,
setState(value) {
this.state = value;
render();
},
});
}
return [state.state, state.setState.bind(state)];
}
function useHook(callback) {
Hook.current = {
__proto__: Hook.current,
};
try {
callback();
} finally {
Hook.current = Hook.current.__proto__;
}
}
function render() {
useHook(() => {
const [count, setCount] = useState(0);
console.log('count:', count);
setTimeout(() => {
setCount(count + 1);
}, 1000);
});
}
render();
In this example, Hook
the object has two important properties: queue
and current
. queue
Store all Hook
state and update functions in the component, and current
store a linked list of components currently being rendered Hook
. useState
and useHook
functions are responsible for creating new Hook
state and using it in the component , respectively Hook
.
Preset Hook function
useState Hook
Here is useState Hook
an example implementation of :
function useState(initialState) {
const hook = updateWorkInProgressHook();
if (!hook.memoizedState) {
hook.memoizedState = [
typeof initialState === 'function' ? initialState() : initialState,
action => {
hook.queue.pending = true;
hook.queue.dispatch = action;
scheduleWork();
},
];
}
return hook.memoizedState;
}
The above code is implemented useState Hook
, and its main function is to return an state
array with the update function, and the initial value of the state is initialState
.
In this implementation, updateWorkInProgressHook()
the function is used to obtain the fiber object of the currently executing function component and determine whether there is a corresponding one hook
. It is implemented as follows:
function updateWorkInProgressHook() {
const fiber = getWorkInProgressFiber();
let hook = fiber.memoizedState;
if (hook) {
fiber.memoizedState = hook.next;
hook.next = null;
} else {
hook = {
memoizedState: null,
queue: {
pending: null,
dispatch: null,
last: null,
},
next: null,
};
}
workInProgressHook = hook;
return hook;
}
getWorkInProgressFiber()
The function is used to obtain the object of the currently executing function component fiber
, workInProgressHook
and is used to store the currently executing hook
object. In a function component, each useState
call creates a new hook object and adds it to the linked list fiber
of objects hooks
. This hooks
linked list is maintained through the properties fiber
of the object .memoizedState
We also need to note that in useState Hook
the implementation of , each hook
object contains an queue
object to store the state to be updated and the update function. scheduleWork()
Functions are used to notify React
the scheduler that a task needs to be executed.
In React
the source code of , useState
the function is actually an useStateImpl
internal function called .
Here is useStateImpl
the source code:
function useStateImpl<S>(initialState: (() => S) | S): [S, Dispatch<SetStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
It can be seen that useStateImpl
the function of the function is to obtain the current dispatcher
and call its useState
method, and return an array, the first element is the value of the state, and the second element is a dispatch
function to update the state. The function here resolveDispatcher
is used to get the current one dispatcher
, and its implementation is as follows:
function resolveDispatcher(): Dispatcher {
const dispatcher = currentlyRenderingFiber?.dispatcher;
if (dispatcher === undefined) {
throw new Error('Hooks can only be called inside the body of a function component. (https://fb.me/react-invalid-hook-call)');
}
return dispatcher;
}
resolveDispatcher
fiber
The function first tries to get the properties of the object currently being rendered dispatcher
, and if it cannot get it, it says
If the component is not currently in the rendering process, an error will be thrown.
Finally, let's take a look at how the method is implemented useState
in a specific implementation. dispatcher
we useReducer
take
dispatcher
For example, it is implemented as follows:
export function useReducer<S, A>(
reducer: (prevState: S, action: A) => S,
initialState: S,
initialAction?: A,
): [S, Dispatch<A>] {
const [dispatch, currentState] = updateReducer<S, A>(
reducer,
// $FlowFixMe: Flow doesn't like mixed types
[initialState, initialAction],
// $FlowFixMe: Flow doesn't like mixed types
reducer === basicStateReducer ? basicStateReducer : updateStateReducer,
);
return [currentState, dispatch];
}
As you can see, useReducer
the method actually calls a updateReducer
function called , which returns an dispatch
array containing the current state and function. updateReducer
The implementation of is more complicated and involves a lot of details, so I won't introduce it here.
useEffect Hook
useEffect
Is a function React
commonly used in components to perform side-effect operations in components, such as accessing remote data, adding/removing event listeners, manual operations , and so on. The core function of is to execute the callback function asynchronously after the rendering process of the component ends, and its implementation involves the asynchronous rendering mechanism in React.Hook
DOM
useEffect
The following is an example implementation of useEffect Hook:
function useEffect(callback, dependencies) {
// 通过调用 useLayoutEffect 或者 useEffect 方法来获取当前的渲染批次
const batch = useContext(BatchContext);
// 根据当前的渲染批次判断是否需要执行回调函数
if (shouldFireEffect(batch, dependencies)) {
callback();
}
// 在组件被卸载时清除当前 effect 的状态信息
return () => clearEffect(batch);
}
In this example, useEffect
two parameters are received: a callback function and an array of dependencies. When any value in the dependencies array changes,
React
useEffect
The callback function passed in will be re-executed at the next rendering .
useEffect
The implementation of the function mainly depends on React
the asynchronous rendering mechanism in . When a component needs to be re-rendered, React
all state
update operations will be added to a queue, and these update operations will be executed asynchronously after the end of the current rendering batch, thereby avoiding multiple consecutive update operations in the same rendering batch.
In useEffect
the function, we useContext(BatchContext)
get the current rendering batch by calling the method, and shouldFireEffect
judge whether the callback function needs to be executed according to the method. After the callback function is executed, we need to use clearEffect
methods to clear the current effect
state information to avoid affecting subsequent rendering batches.
Summarize
In general, React Hooks
the implementation principle of the system is not complicated, it mainly depends on React
the internal fiber
data structure and scheduling system, through these mechanisms to realize the management and update of the component state. Hooks
It allows us to use state and other features in function components React
, making function components comparable to class components.
In addition to useState
, useEffect
etc. hook
, React
there are useContext
other commonly used ones Hook
. Their implementation principles are basically similar, and they all use fiber
the architecture to implement functions such as state management and lifecycle hooks.
The above are hook
simple implementation examples, they are not React
the actual codes used in , but they can help us better understand hook
the core implementation.