根据react的使用自己实现一个hooks
react源码中的实现
react hooks中的数据结构
export type Hook = {|
memoizedState: any, // 当前state的值
baseState: any,
baseQueue: Update<any, any> | null,
queue: UpdateQueue<any, any> | null, // 更新队列
next: Hook | null, // 指针指向下一个hook对象
|};
export type Effect = {|
tag: HookFlags, // 当前effct的类型,触发的阶段
create: () => (() => void) | void, // useEffect回调
destroy: (() => void) | void, // useEffect回调的返回函数
deps: Array<mixed> | null, // 依赖
next: Effect, // 指针指向下一个effect
|};
type UpdateQueue<S, A> = {|
pending: Update<S, A> | null, //记录的是update的链表
dispatch: (A => mixed) | null,
lastRenderedReducer: ((S, A) => S) | null,
lastRenderedState: S | null,
|};
Dispatcher来源
根据renderWithHooks
中fiber
, currentFiber,当前环境进行区分;
讲一下mountHookTypesDev, updateHooks
// src/react/packages/react-reconciler/src/ReactFiberHooks.old.js
export function renderWithHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
props: Props,
secondArg: SecondArg,
nextRenderLanes: Lanes,
): any {
renderLanes = nextRenderLanes;
currentlyRenderingFiber = workInProgress;
if(__DEV__) {
...
}
else {
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;}
理解bind(null, …args)
默认使用下一个函数的传参作为参数;
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
传入的reducer,baseState,是用来在update时候进行优化的;
useEffect相关问题
问题一:
为什么在dep没有改变的情况下,不走useEffect的回调,原理是什么?
看一个简单的例子:
import * as React from "react";
import * as ReactDOM from "react-dom";
import {Component, useState, useEffect} from "react";
export default class ClassFunctionComponent extends Component {
render() {
return (
<div>
<h3>ClassFunctionComponent</h3>
<FunctionComponent />
</div>
);
}
}
function FunctionComponent(props) {
const [count, setCount] = useState(0);
const [val, setVal] = useState("");
const add = () => {
setCount(count + 1);
};
useEffect(() => {
debugger
console.log("useEffect", count); //sy-log
}, [count]);
const handleChange = e => {
setVal(data => e.target.value);
};
return (
<div className="border">
<h3>FunctionComponent</h3>
<p>{count}</p>
<button onClick={add}>add</button>
<input type="text" value={val} onChange={handleChange} />
</div>
);
}
useEffect
在源码中调用的是
src/react/packages/react-reconciler/src/ReactFiberHooks.old.js
function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
undefined,
nextDeps,
);
}
function updateEffectImpl(fiberFlags, hookFlags, create, deps): void {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
if (currentHook !== null) {
const prevEffect = currentHook.memoizedState;
destroy = prevEffect.destroy;
if (nextDeps !== null) {
const prevDeps = prevEffect.deps;
if (areHookInputsEqual(nextDeps, prevDeps)) {
pushEffect(hookFlags, create, destroy, nextDeps);
return;
}
}
}
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushEffect(
HookHasEffect | hookFlags,
create,
destroy,
nextDeps,
);
}
对于依赖没有改变的情况,不需要挂到hook.memoizedState
上,只是执行了pushEffect
,只有在依赖变化时候,pushEffect
的参数才会带上hookHasEffect
去执行后续