React hooks-勝つか負けか?

画像







新しいReact 16.6.0のリリースにより、ドキュメントにフック提案が登場しました。 それらは現在、react 17.0.0-alphaで利用可能であり、オープンRFC:React Hooksで議論されています 。 それが何であり、なぜそれがカットの下で必要であるかを見てみましょう。







はい、これはRFCです。このアプローチを選んだ理由をリアクションの作成者と話し合うことで、最終的な実装に影響を与えることができます。







標準のフックがどのように見えるか見てみましょう:







import { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
      
      





このコードについて考えてみてください。これはティーザーであり、記事の終わりまでに、それが何を意味するかをすでに理解できます。 最初に知っておくべきことは、これによって後方互換性が損なわれることはなく、RFCでフィードバックと提案を収集した後、16.7で追加される可能性があることです。







みんなが保証するように、これはクラスを試薬から切り落とす計画ではありません。







また、フックは反応の現在の概念を置き換えるものではなく、すべてが小道具/状態/コンテキスト/参照の代わりになります。 これは、彼らの力を使用する別の方法です。







やる気



フックは、Facebookで5年間にわたって何万ものコンポーネントをサポートすることで発生した非接続問題を一見して解決します。







最も難しいのは、ステートフルコンポーネントでロジックを再利用することです。リアクションには、コンポーネントに再利用可能な動作を付加する方法がありません(たとえば、リポジトリに接続する)。 Reactを使用したことがあれば、HOC(高次コンポーネント)またはレンダープロップの概念を理解しています。 これらは十分なパターンですが、過度に使用される場合があり、使用できるようにコンポーネントを再構築する必要があり、通常はコードが面倒になります。 典型的な反応アプリケーションを見る価値があり、何が危険なのかが明らかになります。







画像







これは、 wrapped-hell -wrapper hellと呼ばます。







HOCのみからのアプリケーションは、現在の現実では普通であり、コンポーネントをストア/テーマ/ローカライズ/カスタムホックに接続します。これは誰もが知っていると思います。







反応がロジックを分離するために別の原始的なメカニズムを必要とすることが明らかになります。







フックを使用して、コンポーネントの状態を取得し、テストして再利用できるようにします。 フックを使用すると、コンポーネントの階層を変更せずに状態ロジックを再利用できます。 これにより、多くのコンポーネント間またはシステム全体間のリンクの交換が容易になります。 また、クラスコンポーネントは非常に恐ろしく見えます。ライフサイクルメソッドcomponentDidMount



/ shouldComponentUpdate



/ componentDidUpdate



componentDidUpdate



の状態、state / storを操作するメソッドの作成、コンポーネントインスタンスのbindimメソッドなどについて説明します。 通常、そのようなコンポーネントはx行を超えます。xは理解するのに十分困難です。







フックを使用すると、コンポーネント間のロジックを小さな関数に分割し、コンポーネント内で使用することで同じことができます。







クラスは人にとっても車にとっても難しい



Facebookのクラスを観察することは、Reactを学習する際の大きな障害です。 this



がどのthis



機能するかを理解する必要があり、他のプログラミング言語のように機能しないため、イベントハンドラーのバインドについても覚えておく必要があります。 安定した構文文がないと、コードは非常に冗長に見えます。 人々は小道具/状態パターンといわゆるトップダウンデータフローを非常によく理解していますが、クラスを理解するのは困難です。







特に、テンプレートに限定されない場合、それほど前のことではないが、反応の男はPrepack を使用してコンポーネントレイアウトを実験し、有望な結果を見たが、それにもかかわらず、クラスコンポーネントを使用すると、これらの最適化が失われるような意図しない悪いパターンを作成でき、クラスも非常にうまく移行しないホットリロードクラスは信頼性を低下させます。 まず、すべての最適化をサポートし、ホットリブートで正常に動作するAPIを提供したかったのです。







フックを見てください



状態フック



以下のコードは段落とボタンをレンダリングします。ボタンをクリックすると、段落の値が増加します。







 import { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
      
      





このことから、このフックはstate



などの概念でも同様に機能すると結論付けることができます。

もう少し詳細なuseState



メソッドは1つの引数を取ります。これはデフォルト値であり、値自体とそれを変更するメソッドがあるタプルを返します。setStateとは異なり、setCountは値をマージせず、単に更新します。 複数の状態宣言を使用することもできます。たとえば、







 function ExampleWithManyStates() { // Declare multiple state variables! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // ... }
      
      





したがって、一度に複数の状態を作成し、それらをどうにか分解する方法を考える必要はありません。 したがって、フックはクラスコンポーネントのチップに「接続」できる関数であり、クラス内でフックが機能しないのと同様に、これを覚えておくことが重要です。







エフェクトフック



多くの場合、クラスコンポーネントでは、副作用関数を作成します。たとえば、イベントにサブスクライブしたり、データを要求したりします。通常、このためにcomponentDidMount



/ componentDidUpdate



メソッドを使用しcomponentDidMount









 import { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
      
      





useEffect



を呼び出すとき、DOMツリーの変更を更新した後、「副作用」を実行するようにリアクションに伝えます。 効果はコンポーネント内で宣言されるため、小道具/状態にアクセスできます。 そして、あなたが好きなだけ同じ方法でそれらを作成することができます。







 function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); function handleStatusChange(status) { setIsOnline(status.isOnline); } // ...
      
      





すぐに、2番目の副作用に注意する価値があります。関数を返します。コンポーネントがアンマウントを実行した後、アクションを実行するためにこれを行います。新しいAPIでは、これはクリーニング付きの効果と呼ばれます。 他の効果は何でも返すことができます。







フックルール



フックは単なるJavaScript関数ですが、必要なルールは2つだけです。









カスタムフック



同時に、ステートフルコンポーネントのロジックを再利用したいと思います。通常は、HOCまたはレンダリングの小道具パターンのいずれかを使用しますが、アプリケーションの追加ボリュームを作成します。

たとえば、次の関数について説明します。







 import { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) { setIsOnline(status.isOnline); } useEffect(() => { ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; }
      
      





このコードを実現します。さまざまなコンポーネントで呼び出すことができるカスタムフックになります。 たとえば、次のように:







 function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
      
      





かそこら







 function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); }
      
      





いずれの場合も、コンポーネントの状態を再利用し、 useFriendStatus



関数の呼び出しごとに分離状態を作成します。 また、この関数の先頭はuseという単語で始まっていることに注意してください。これはフックであることを意味します。 この形式に従うことをお勧めします。 何でも、アニメーション/サブスクリプション/タイマーなどのためのカスタムフックを書くことができます。







さらにいくつかのフックがあります。







useContext



useContext



を使用すると、renderPropsの代わりに通常の戻り値を使用できます。抽出するコンテキストを渡す必要があります。これを返すと、コンテキストをpropに渡したすべてのHOCを取り除くことができます。







 function Example() { const locale = useContext(LocaleContext); const theme = useContext(ThemeContext); // ... }
      
      





これで、戻り値でコンテキストオブジェクトを使用できます。







useCallback



 const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], );
      
      





メソッドへの参照を保存するためだけに、クラスのコンポーネントを作成しなければならなかった頻度はどれくらいですか? これはもう行う必要はありません。useCallbackを使用できます。onClickへの新しいリンクが到着したため、コンポーネントは再描画されません。







useMemo



memized値を返します。memized値は、引数の1つが変更された場合にのみ計算され、2回目は同じものが計算されないことを意味します。







 const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
      
      





はい、ここでは、フックが変更されていないことを理解できるように、配列内の値を複製する必要があります。







useRef



useRef



は変更された値を返します。ここで、 .current



フィールドは最初の引数で初期化され、コンポーネントが存在する限りオブジェクトは存在します。

入力に注目した場合の最も一般的な例







 function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { // `current` points to the mounted text input element inputEl.current.focus(); }; return ( <> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </> ); }
      
      





useImperativeMethods



useImperativeMethods



は、親から渡されるインスタンスの値をカスタマイズし、refを直接使用します。 いつものように、直接リンクは避け、 forwardRef



を使用する必要があります







 function FancyInput(props, ref) { const inputRef = useRef(); useImperativeMethods(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} ... />; } FancyInput = forwardRef(FancyInput);
      
      





この例では、 FancyInput



FancyInput



コンポーネントFancyInput



fancyInputRef.current.focus()



呼び出すことができます。







useMutationEffect



useMutationEffect



非常に似ていますが、reactが隣接するコンポーネントが更新される前にDOM値を変更する段階で同期的に開始されることを除き、このフックはDOMミューuseEffect



を実行するために使用されます。

UseEffectは、視覚的な変更のブロックを防ぐために最適です。







useLayoutEffect



useLayoutEffect



useLayoutEffect



と似ていますが、すべてのDOMおよび同期再レンダリングの更新後に同期的開始する点がuseEffect



ますuseLayoutEffect



計画された更新は、ブラウザーが要素を描画する前に同期的に適用されます。 また、視覚的な変更を妨げないように、標準のuseEffect



使用を試みる必要があります。







useReducer



useReducer



は、状態と変更をディスパッチする機能を返すレデューサーを作成するためのフックです。







 const [state, dispatch] = useReducer(reducer, initialState);
      
      





Reduxの仕組みを理解していれば、 useReducer



仕組みを理解できます。 useReducer



介してのみ上記のカウンターを使用したのと同じ例:







 const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'reset': return initialState; case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; } } function Counter({initialCount}) { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset'})}> Reset </button> <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); }
      
      





UseReducerも3つの引数を取ります。これは、レデューサーが初期化されたときに実行されるaction



です。







 const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'reset': return {count: action.payload}; case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; } } function Counter({initialCount}) { const [state, dispatch] = useReducer( reducer, initialState, {type: 'reset', payload: initialCount}, ); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button> <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); }
      
      





このレデューサーでコンテキストを作成し、 useContext



フックを使用してアプリケーション全体で使用することもできます。これは宿題用のままです。







まとめると



フックは、wrapper-hellを解決し、いくつかの問題を解決するための非常に強力なアプローチですが、それらすべてをリンク転送の 1つの定義で使用できます。 現在、使用するフックのコレクションまたはこのコレクションが表示され始めています。 フックの詳細については、 ドキュメントをご覧ください。








All Articles