基本フック
1.使用状態
状態と、状態を更新する関数を返します。
最初のレンダリング中に返される状態 (state) は、渡された最初のパラメーター (initialState) と同じ値です。
状態を更新するには setState 関数を使用します。新しい状態値を受け取り、コンポーネントの再レンダリングをキューに入れます。
その後の再レンダリングでは、useState によって返される最初の値は、常に最新の更新された状態になります。
React は、setState 関数の ID が安定しており、コンポーネントの再レンダリング間で変化しないことを保証します。そのため、useEffect または useCallback の依存関係リストから setState を安全に省略できます。
const [state, setState] = useState(initialState);
setState(newState);
機能更新
前の状態を使用して新しい状態を計算する必要がある場合は、関数を setState に渡すことができます。この関数は前の状態を取り、更新された値を返します。以下のカウンタ コンポーネントの例は、setState の 2 つの使用法を示しています。
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
更新された状態は前の状態に基づく必要があるため、「+」ボタンと「-」ボタンは機能します。ただし、「リセット」ボタンは通常の形式をとります。これは、カウントを常に初期値に戻すためです。
更新関数が現在の状態とまったく同じ状態を返す場合、その後の再レンダリングは完全にスキップされます。
知らせ:
クラス コンポーネントの setState メソッドとは異なり、useState は更新されたオブジェクトを自動的にマージしません。関数 setState をスプレッド演算子と組み合わせて使用すると、オブジェクトのマージと更新の効果を得ることができます。
与 class 组件中的 setState 方法不同,useState 不会自动合并更新对象。你可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果。
useReducer は別の代替手段であり、複数のサブ値を持つ状態オブジェクトの管理により適しています。
怠惰な初期状態
initialState パラメーターは、コンポーネントの最初のレンダリングでのみ機能し、後続のレンダリングでは無視されます。複雑な計算によって初期状態を取得する必要がある場合は、関数を渡し、関数で初期状態を計算して返すことができます。この関数は、初期レンダリング中にのみ呼び出されます。
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
状態の更新をスキップ
State Hook の update 関数を呼び出して現在の状態を渡すと、React はサブコンポーネントのレンダリングとエフェクトの実行をスキップします。(React はObject.is 比較アルゴリズムを使用して状態を比較します。)
React は、レンダリングをスキップする前にコンポーネントをレンダリングする必要がある場合があることに注意してください。しかし、React はコンポーネント ツリーの「深い」ノードを不必要にレンダリングしないため、心配する必要はありません。レンダリング中にコストのかかる計算を実行する場合は、 useMemo を使用して最適化できます
2.使用効果
useEffect(didUpdate);
このフックは、命令型の、おそらく副作用のあるコードを含む関数を受け入れます。
DOM の変更、サブスクリプションの追加、タイマーの設定、ロギング、および関数コンポーネントの本体内での副作用を伴うその他の操作の実行 (ここでは React レンダリング フェーズ中) は許可されていません。これは、不可解なバグを引き起こし、UI の一貫性を損なう可能性があるためです。
useEffect を使用して、副作用を完成させます。useEffect に割り当てられた関数は、コンポーネントが画面にレンダリングされた後に実行されます。効果は、React の純粋に機能的な世界から命令的な世界への脱出経路と考えることができます。
デフォルトでは、エフェクトは各レンダリング ラウンドの後に実行されますが、特定の値が変更された場合にのみ実行するように選択できます。
クリアエフェクト
通常、サブスクリプション ID やタイマー ID などのエフェクトによって作成されたリソースは、コンポーネントのアンマウント時にクリーンアップする必要があります。これを行うために、useEffect 関数はクリーンアップ関数を返します。以下は、サブスクリプションを作成する例です。
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// 清除订阅
subscription.unsubscribe();
};
});
メモリ リークを防ぐために、コンポーネントがアンマウントされる前にクリーンアップ関数が実行されます。また、コンポーネントが複数回レンダリングされる場合 (通常はそうです)、前の効果は次の効果が実行される前にクリアされます。上記の例では、これは、コンポーネントの更新ごとに新しいサブスクリプションが作成されることを意味します。
効果の発動タイミング
componentDidMount や componentDidUpdate とは異なり、 useEffect に渡される関数は、ブラウザーがレイアウトと描画を終了した後に遅延します。これにより、サブスクリプションの設定やイベント処理など、多くの一般的な副作用のシナリオに適しているため、ブラウザーが画面を更新するのをブロックする操作を関数で実行しないでください。
ただし、すべての効果を遅らせることはできません。たとえば、ユーザーに表示される DOM の変更は、ブラウザーが次の描画を実行する前に同期的に実行する必要があります。これにより、ユーザーが視覚的な不整合を経験することはありません。(イベントを受動的にリッスンすることとイベントをアクティブにリッスンすることの違いと概念的に似ています。) React は、このタイプの効果を処理するための追加のuseLayoutEffectフックを提供します。useEffectと構造は同じで、違うのは呼び出すタイミングだけです。
useEffect はブラウザーの描画後に遅延して実行されますが、新しいレンダリングの前に実行されることが保証されています。React は、コンポーネントが更新される前に、最後のレンダリングから効果を更新します。
効果の条件付き実行
デフォルトでは、エフェクトはコンポーネント レンダリングの各ラウンドの後に実行されます。このように、効果の依存関係が変更されるたびに、再作成されます。
コンポーネントが更新されるたびに新しいサブスクリプションを作成する代わりに、ソース プロパティが変更されたときに再作成するだけで済みます。
これを実現するために、useEffect に 2 番目のパラメーターを渡すことができます。これは、効果が依存する値の配列です。更新された例は次のとおりです
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);
この時点で、サブスクリプションは props.source が変更された場合にのみ再作成されます。
3.useContext
const value = useContext(MyContext);
コンテキスト オブジェクト (React.createContext の戻り値) を受け取り、コンテキストの現在の値を返します。現在のコンテキスト値は、上部コンポーネントの現在のコンポーネントに最も近い <MyContext.Provider> の value prop によって決定されます。
コンポーネントの上の最新の <MyContext.Provider> が更新されると、このフックは再レンダリングをトリガーし、MyContext プロバイダーに渡された最新のコンテキスト値を使用します。祖先がReact.memoまたはshouldComponentUpdateを使用していても、コンポーネント自体が useContext を使用すると再レンダリングされます。
useContext のパラメーターは、コンテキスト オブジェクト自体でなければならないことを忘れないでください。
正しい: useContext(MyContext)
エラー: useContext(MyContext.Consumer)
エラー: useContext(MyContext.Provider)
useContext を呼び出すコンポーネントは、コンテキスト値が変更されると常に再レンダリングされます。コンポーネントの再レンダリングにコストがかかる場合は、 memoization を使用して。
ヒント:
フックに連絡する前にコンテキスト API に精通している場合は、useContext(MyContext) がクラス コンポーネントの static contextType = MyContext または <MyContext.Consumer> と同等であることは理解できるはずです。
useContext(MyContext) を使用すると、コンテキスト値を読み取り、コンテキストの変更をサブスクライブできます。下位のコンポーネントにコンテキストを提供するには、上位のコンポーネント ツリーで <MyContext.Provider> を使用する必要があります。
const themes = {
light: {
foreground: "#000000",
background: "#eeeeee"
},
dark: {
foreground: "#ffffff",
background: "#222222"
}
};
const ThemeContext = React.createContext(themes.light);
function App() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={
{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
追加のフック
useReducer
useStateの代替。(state, action) => newState の形式でレデューサーを受け取り、現在の状態とそのディスパッチ メソッドを返します。(Redux に精通している場合は、その仕組みを既に知っています。)
一部のシナリオでは、useReducer は useState よりも適しています。たとえば、状態ロジックが複雑で複数のサブ値が含まれている場合や、次の状態が前の状態に依存している場合などです。また、コールバック関数の代わりにディスパッチをサブコンポーネントに渡すことができるため、useReducer を使用すると、ディープ アップデートをトリガーするコンポーネントのパフォーマンスを最適化することもできます。
const [state, dispatch] = useReducer(reducer, initialArg, init);
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
React は、ディスパッチ関数の ID が安定しており、コンポーネントの再レンダリング間で変化しないことを保証します。これが、useEffect または useCallback の依存関係リストからディスパッチを安全に省略できる理由です。
初期状態を指定する
useReducer の状態を初期化するには 2 つの方法があり、使用シナリオに応じていずれかを選択できます。useReducer の 2 番目の引数として初期状態を渡すのが最も簡単な方法です。
const [state, dispatch] = useReducer(
reducer,
{count: initialCount}
);
遅延初期化
初期状態を遅延して作成することを選択できます。これを行うには、init 関数を useReducer の 3 番目のパラメーターとして渡す必要があります。これにより、初期状態が init(initialArg) に設定されます。
function init(initialCount) {
return {count: initialCount};
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
Count: {state.count}
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
発送をスキップ
Reducer Hook の戻り値が現在の状態と同じである場合、React は子コンポーネントのレンダリングと副作用の実行をスキップします。(React はObject.is 比較アルゴリズムを使用して状態を比較します。)
React は、レンダリングをスキップする前にコンポーネントを再度レンダリングする必要がある場合があることに注意してください。しかし、React はコンポーネント ツリーの「深い」ノードを不必要にレンダリングしないため、心配する必要はありません。レンダリング中にコストのかかる計算を実行する場合は、useMemo を使用して最適化できます。
useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
メモ化されたコールバック関数を返す
インライン コールバック関数と依存関係の配列をパラメーターとして useCallback に渡すと、依存関係が変更された場合にのみ更新されるメモ化されたバージョンのコールバック関数が返されます。最適化された子コンポーネントにコールバックを渡し、参照の等価性を使用して不要なレンダリングを回避する場合に役立ちます (例: shouldComponentUpdate)。
useCallback(fn, deps) は useMemo(() => fn, deps) と同等です。
メモを使う
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
メモ化された値を返します。
「作成」関数と依存関係配列をパラメーターとして useMemo に渡すと、依存関係の 1 つが変更された場合にのみメモ化された値が再計算されます。この最適化により、すべてのレンダリングでコストのかかる計算を回避できます。
useMemo に渡された関数は、レンダリング中に実行されることに注意してください。この関数内でレンダリングに関係のない操作を行わないでください. 副作用などの操作は useMemo ではなく useEffect の適用範囲に属します.
依存配列が提供されていない場合、useMemo はすべてのレンダリングで新しい値を計算します。
パフォーマンスの最適化として useMemo を使用できますが、セマンティックの保証としては使用しないでください。将来、React は以前にメモ化された値を「忘れて」、次のレンダリングでそれらを再計算することを選択する可能性があります (たとえば、オフスクリーン コンポーネント用にメモリを解放するため)。最初に useMemo なしで実行するコードを作成し、後でコードに useMemo を追加して、パフォーマンスを最適化します。
useRef
const refContainer = useRef(initialValue);
useRef は、渡されたパラメーター (initialValue) に初期化された .current プロパティを持つ変更可能な ref オブジェクトを返します。返された ref オブジェクトは、コンポーネントの有効期間を通じて持続します。
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
基本的に、useRef は、可変値を .current プロパティに保持できる「ボックス」のようなものです。
DOM にアクセスする主な方法である ref に精通している必要があります。ref オブジェクトを <div ref={myRef} /> としてコンポーネントに渡すと、ノードがどのように変化しても、React は ref オブジェクトの .current プロパティを対応する DOM ノードに設定します。
ただし、useRef() は ref 属性よりも便利です。これは、インスタンス フィールドがクラスで使用される方法と同様に、変更可能な値を格納する便利な方法です。
これは、プレーンな Javascript オブジェクトを作成するためです。useRef() と自己構築の {current: ...} オブジェクトの唯一の違いは、useRef がレンダリングされるたびに同じ ref オブジェクトを返すことです。
ref オブジェクトのコンテンツが変更されても、 useRef は通知しないことに注意してください。.current プロパティを変更しても、コンポーネントは再レンダリングされません。React が DOM ノードの ref をバインドまたはアンバインドするときに何らかのコードを実行したい場合は、コールバック ref を使用して達成する必要があります。
useImperativeHandle
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle を使用すると、ref を使用するときに親コンポーネントに公開されるインスタンス値をカスタマイズできます。ほとんどの場合、ref のような命令型コードは避けるべきです。useImperativeHandle はforwardRefと一緒に使用する必要があります:
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
この例では、<FancyInput ref={inputRef} /> をレンダリングする親コンポーネントは、inputRef.current.focus() を呼び出すことができます。
useLayoutEffect
その関数シグネチャは useEffect と同じですが、すべての DOM 変更後にエフェクトを同期的に呼び出します。DOM レイアウトを読み取り、再レンダリングを同期的にトリガーするために使用できます。useLayoutEffect 内の更新計画は、ブラウザーが描画を実行する前に同期的に更新されます。
視覚的な更新を妨げないように、可能な限り標準の useEffect を使用してください。
useDebugValue
useDebugValue(value)
useDebugValue を使用して、React DevTools でカスタム フックのラベルを表示できます。
各カスタム フックにデバッグ値を追加することはお勧めしません。共有ライブラリの一部である場合に最も価値があります。
デバッグ値のレイジーフォーマット
場合によっては、フォーマットされた値の表示はコストのかかる操作になる可能性があります。フックをチェックする必要がない限り、これを行う必要はありません。
したがって、useDebugValue は、オプションの 2 番目の引数として書式設定関数を受け入れます。この関数は、フックがチェックされている場合にのみ呼び出されます。デバッグ値を引数として受け取り、フォーマットされた表示値を返します。
たとえば、Date 値を返すカスタム フックは、format 関数を介して不要な toDateString 関数呼び出しを回避できます。
useDebugValue(date, date => date.toDateString());