著者: JD 小売鄭ビンイー
序文
React Hooks
はReact
16.8 で導入された新機能で、state
クラス コンポーネントを使用せずに関数コンポーネントで他の React 機能を使用できるようにします。Hooks
は、よりシンプルでわかりやすいReact
開発エクスペリエンスを提供するため、非常に重要な概念です。
React Hooks
コア ソース コードには、主に 2 つの部分が含まれています。React
内部Hook
マネージャーと一連のプリセットHook
機能です。
React
まず、内部のHook
マネージャーを見てみましょう。このマネージャーは、React
コンポーネント内のすべてを管理しHook
、コンポーネントのレンダリング中にそれらが正しい順序で呼び出されるようにする重要な内部メカニズムです。
内部フック マネージャー
例:
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();
この例では、Hook
オブジェクトにqueue
との 2 つの重要なプロパティがありますcurrent
。queue
すべてのHook
状態関数と更新関数をコンポーネントに保存し、current
現在レンダリングされているコンポーネントのリンク リストを保存しますHook
。useState
関数と関数は、それぞれ新しい状態を作成し、それをコンポーネントで使用するuseHook
責任があります。Hook
Hook
プリセットフック機能
useState フック
useState Hook
の実装例を次に示します。
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;
}
上記のコードは実装されておりuseState Hook
、その主な機能は update 関数で配列を返すことでありstate
、状態の初期値は ですinitialState
。
この実装では、updateWorkInProgressHook()
関数を使用して、現在実行中の関数コンポーネントのファイバー オブジェクトを取得し、対応するものがあるかどうかを判断しますhook
。次のように実装されます。
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()
関数は、現在実行中の関数コンポーネントのオブジェクトを取得するために使用されfiber
、workInProgressHook
現在実行中のオブジェクトを格納するために使用されますhook
。関数コンポーネントでは、useState
呼び出しごとに新しいフック オブジェクトが作成され、fiber
オブジェクトのリンク リストに追加されますhooks
。このhooks
リンク リストは、fiber
オブジェクトのmemoizedState
プロパティを通じて維持されます。
useState Hook
の実装では、各hook
オブジェクトには、queue
更新する状態と update 関数を格納するためのオブジェクトが含まれていることにも注意する必要があります。関数は、タスクを実行する必要があることをスケジューラにscheduleWork()
通知するために使用されます。React
React
のソース コードでは、useState
この関数は実際にはuseStateImpl
と呼ばれる内部関数です。
useStateImpl
ソースコードは次のとおりです。
function useStateImpl<S>(initialState: (() => S) | S): [S, Dispatch<SetStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
useStateImpl
関数の機能は、現在の値dispatcher
を取得してそのuseState
メソッドを呼び出し、配列を返すことであり、最初の要素は状態の値であり、2 番目の要素はdispatch
状態を更新する関数であることがわかります。ここの関数はresolveDispatcher
現在の関数を取得するために使用されdispatcher
、その実装は次のとおりです。
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
オブジェクトのプロパティを取得しようとしますdispatcher
。取得できない場合は、
コンポーネントが現在レンダリング プロセスにない場合、エラーがスローされます。
useState
最後に、特定の実装でdispatcher
メソッドがどのように実装されているかを見てみましょう。私たちはuseReducer
取る
dispatcher
たとえば、次のように実装されます。
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];
}
ご覧のとおり、useReducer
このメソッドは実際にはupdateReducer
という関数を呼び出します。このdispatch
関数は、現在の状態と関数を含む配列を返します。updateReducer
の実装はより複雑で詳細が多いため、ここでは紹介しません。
useEffect フック
useEffect
リモートデータへのアクセス、イベントリスナーの追加/削除、手動操作など、コンポーネントで副作用操作を実行するためにコンポーネントReact
で一般的に使用されるHook
関数です。のコア機能は、コンポーネントのレンダリング プロセスが終了した後にコールバック関数を非同期で実行することであり、その実装には React の非同期レンダリング メカニズムが含まれます。DOM
useEffect
以下は、useEffect フックの実装例です。
function useEffect(callback, dependencies) {
// 通过调用 useLayoutEffect 或者 useEffect 方法来获取当前的渲染批次
const batch = useContext(BatchContext);
// 根据当前的渲染批次判断是否需要执行回调函数
if (shouldFireEffect(batch, dependencies)) {
callback();
}
// 在组件被卸载时清除当前 effect 的状态信息
return () => clearEffect(batch);
}
この例では、useEffect
コールバック関数と依存関係の配列の 2 つのパラメーターを受け取ります。依存関係配列のいずれかの値が変更されると、
React
useEffect
渡されたコールバック関数は、次のレンダリングで再実行されます。
useEffect
関数の実装は、主に のReact
非同期レンダリング メカニズムに依存します。コンポーネントを再レンダリングする必要がある場合、React
すべてのstate
更新操作がキューに追加され、これらの更新操作は現在のレンダリング バッチの終了後に非同期で実行されるため、同じレンダリング バッチで複数の連続した更新操作が回避されます。
useEffect
関数では、useContext(BatchContext)
メソッドを呼び出して現在のレンダリング バッチを取得し、shouldFireEffect
メソッドに従ってコールバック関数を実行する必要があるかどうかを判断します。コールバック関数が実行された後、後続のレンダリング バッチに影響を与えないようにclearEffect
、メソッドを使用して現在のeffect
状態情報をクリアする必要があります。
要約する
一般に、React Hooks
システムの実装原理は複雑ではなく、主にReact
内部fiber
データ構造とスケジューリングシステムに依存し、これらのメカニズムを通じてコンポーネントの状態の管理と更新を実現します。Hooks
これにより、関数コンポーネントで状態やその他の機能を使用できるようになりReact
、関数コンポーネントがクラス コンポーネントに匹敵するようになります。
useState
、useEffect
などの ほかhook
、他 に 常用 し て い る もの がReact
あ る。それらの実装原理は基本的に似ており、すべてアーキテクチャを使用して、状態管理やライフサイクル フックなどの機能を実装しています。useContext
Hook
fiber
上記はhook
単純な実装例であり、実際に で使用されるコードではありませんが、コア実装をReact
よりよく理解するのに役立ちます。hook