React Hooks は React 16.8 で導入された新しい機能で、クラスを使用せずに状態やその他の機能を使用できるようになります。 React Hooks が解決しようとしている問題は、状態の共有です。これは、レンダリング プロパティと高階コンポーネントに続く 3 番目の状態ロジック再利用ソリューションであり、JSX ネスト地獄の問題を引き起こしません。 なぜフックなのか?Hooks を紹介する前に、まずは React のコンポーネント作成方法についてお話ししたいと思います。1 つはクラス コンポーネント、もう 1 つは純粋な関数コンポーネントです。React チームは、コンポーネントが複雑なコンテナになるのではなく、データ フローのパイプラインにすぎないことを望んでいます。開発者は必要に応じてパイプラインを組み合わせることができます。つまり、コンポーネントはクラスではなく関数として記述するのが最適です。 。 関数コンポーネントは、クラスコンポーネントよりもビジネスロジックコードの分離やコンポーネントの再利用に便利です。また、関数コンポーネントはクラスコンポーネントよりも軽量です。React Hooks 以前は、関数コンポーネントは LocalState を実装できなかったため、関数コンポーネントを使用して localstate を持つコンポーネントを記述することはできませんでした。これにより、関数コンポーネントの適用範囲が制限されていましたが、React Hooks によって関数コンポーネントの機能が拡張されました。ただし、使用中は次の問題にも注意する必要があります。そうしないと、落とし穴に陥り、パフォーマンスが低下します。これらの罠を回避するには、以下の手順に従ってください。 1. コンポーネント関数の外部にある状態変化に関係のない変数とメソッドを抽出する状態が変化するたびに、関数コンポーネント全体が再実行されます。その結果、関数コンポーネント内で定義されたメソッドと変数が再作成され、それらにメモリが再割り当てされるため、パフォーマンスに影響します。 React をインポートし、{useState、useCallback} を "react" からインポートします。 // 状態が変化するたびにメソッドがメモリを再割り当てすることをテストします。let testFooMemoAlloc = new Set(); const Page = (props:any) => { console.log('状態が変化するたびに、関数コンポーネントは最初から実行されます') 定数[count, setCount] = useState(0); 定数calc = () => { setCount(カウント + 1); } 定数バー = { a:1、 b:2、 c: 「状態に依存しない変数定義」 } 定数doFoo = () => { console.log('状態に関係のないメソッド'); } testFooMemoAlloc.add(doFoo) 戻る ( <> <button onClick={calc}>1 を追加</button> <p>カウント:{count}</p> <p>testFooMemoAlloc.size が増加する場合、メモリが毎回再割り当てされることを意味します: {testFooMemoAlloc.size}</p> </> ) } デフォルトページをエクスポートします。 状態の変更に関連する変数とメソッドはフック コンポーネントに配置する必要がありますが、状態に関連しない変数とメソッドは関数コンポーネントの外部に抽出して、状態が更新されるたびにメモリが再割り当てされるのを防ぐことができます。また、useMemo と useCallback を使用してそれぞれ変数と関数をラップし、同じ効果を実現することもできます。これについては後で説明します。 React をインポートし、{useState、useCallback} を "react" からインポートします。 // 状態が変化するたびにメソッドがメモリを再割り当てすることをテストします。let testFooMemoAlloc = new Set(); 定数バー = { a:1、 b:2、 c: 「状態に依存しない変数定義」 } 定数doFoo = () => { console.log('状態に関係のないメソッド'); } const Page = (props:any) => { console.log('状態が変化するたびに、関数コンポーネントは最初から実行されます') 定数[count, setCount] = useState(0); 定数calc = () => { setCount(カウント + 1); } testFooMemoAlloc.add(doFoo) 戻る ( <> <button onClick={calc}>1 を追加</button> <p>カウント:{count} <p>testFooMemoAlloc.size が増加する場合、メモリが毎回再割り当てされることを意味します: {testFooMemoAlloc.size}</p> </> ) } デフォルトページをエクスポートします。 2. サブコンポーネントをメモでパッケージ化する親コンポーネントによる子コンポーネントの導入により、不要なレンダリングが繰り返されることになります。親コンポーネントがカウントを更新するたびに、子コンポーネントも更新されます。 React をインポートし、{useState} を "react" から取得します。 const 子 = (props:any) => { console.log('サブコンポーネント?') 戻る( <div>私は子コンポーネントです</div> ); } const Page = (props:any) => { 定数[count, setCount] = useState(0); 戻る ( <> <button onClick={(e) => { setCount(count+1) }}>1 追加</button> <p>カウント:{count} <子供 /> </> ) } デフォルトページをエクスポートします。 メモを使用すると、カウント変更サブコンポーネントは更新されません 「react」から React,{useState,memo} をインポートします。 const 子 = memo((props:any) => { console.log('サブコンポーネント?') 戻る( <div>私は子コンポーネントです</div> ); }) const Page = (props:any) => { 定数[count, setCount] = useState(0); 戻る ( <> <button onClick={(e) => { setCount(count+1) }}>1 追加</button> <p>カウント:{count} <子供 /> </> ) } デフォルトページをエクスポートします。 2 番目のパラメータをメモに渡すと、オブジェクトの詳細な比較が可能になります。子コンポーネントによって渡されるプロパティ値が変更されない場合、子コンポーネントは意味のないレンダリングを実行しません。 memo は関数コンポーネントだけでなく、クラスコンポーネントにも適用できます。これは高階コンポーネントです。デフォルトでは、複雑なオブジェクトに対して浅い比較のみを実行します。深い比較を行う場合は、2 番目のパラメータを渡すことができます。 「react」から React、{useState、memo} をインポートします。 「./deepCompare」から deepCompare をインポートします。 const 子 = memo((props:any) => { console.log('サブコンポーネント') 戻る ( <> <div>私は子コンポーネントです</div> <div>{ props.fooObj.a } </div> </> ); }, ディープ比較) const Page = (props:any) => { 定数[count, setCount] = useState(0); const [fooObj, setFooObj] = useState({ a: 1, b: { c: 2 } }) console.log('ページのレンダリングが開始されます') 定数calc = () => { setCount(カウント + 1); (カウント === 3)の場合{ setFooObj({ b: { c: 2 }, a: count }) } } 定数doBar = () => { console.log('メソッドを子コンポーネントに渡して、不要なレンダリングが発生するかどうかをテストします') } 戻る ( <> <button onClick={calc}>1 を追加</button> <p>カウント:{count} <子 fooObj={fooObj} doBar={doBar} /> </> ) } デフォルトページをエクスポートします。 // 2 つのオブジェクトが等しいかどうかを深く比較します export default function deepCompare(prevProps: any, nextProps: any) { const len: 数値 = 引数.長さ; leftChain: any[] = []; とします。 rightChain: any = []; とします。 // // console.log({ 引数 }); // 長さが2以下の場合 // console.log('2 つのオブジェクトのプロパティを比較するには、2 つのオブジェクトを渡す必要があります'); true を返します。 } // (i = 1; i < len; i++ の場合) { // 左チェーン = []; // 右チェーン = []; console.log({ 前のプロパティ、次のプロパティ }); if (!compare2Objects(前のProps、次のProps、左チェーン、右チェーン)) { // console.log('2つのオブジェクトは等しくありません'); false を返します。 } // } // console.log('2つのオブジェクトは等しい'); true を返します。 } 関数 compare2Objects(前のプロパティ: 任意、次のプロパティ: 任意、左チェーン: 任意、右チェーン: 任意) { var p; // 両方の値が NaN の場合、JS では等しくありませんが、ここでは等しいと見なすのが妥当です if (isNaN(prevProps) && isNaN(nextProps) && typeof prevProps === 'number' && typeof nextProps === 'number') { true を返します。 } // 元の値の比較 if (prevProps === nextProps) { console.log('元の値', prevProps, nextProps); true を返します。 } //型の比較を構築if ( (typeof prevProps === 'function' && typeof nextProps === 'function') || (前のProps インスタンス Date && 次のProps インスタンス Date) || (前のプロパティのインスタンス RegExp && 次のプロパティのインスタンス RegExp) || (前のProps インスタンス String && 次のProps インスタンス String) || (前のProps インスタンス オブ ナンバー && 次のProps インスタンス オブ ナンバー) ){ console.log('function', prevProps.toString() === nextProps.toString()); prevProps.toString() === nextProps.toString() を返します。 } // 2つの比較変数の値がnullかつ未定義の場合、ここで終了します if (!(prevProps instanceof Object && nextProps instanceof Object)) { console.log(prevProps, nextProps, 'prevProps instanceof Object && nextProps instanceof Object'); false を返します。 } if (prevProps.isPrototypeOf(nextProps) || nextProps.isPrototypeOf(prevProps)) { console.log('prevProps.isPrototypeOf(nextProps) || nextProps.isPrototypeOf(prevProps)'); false を返します。 } // コンストラクタが等しくない場合、2つのオブジェクトは等しくありません if (prevProps.constructor !== nextProps.constructor) { console.log('前のProps.constructor !== 次のProps.constructor'); false を返します。 } // プロトタイプが等しくない場合、2つのオブジェクトは等しくありません if (prevProps.prototype !== nextProps.prototype) { console.log('前のProps.prototype !== 次のProps.prototype'); false を返します。 } 左チェーンのインデックスが前のプロパティより -1 の場合 || 右チェーンのインデックスが次のプロパティより -1 の場合 console.log('leftChain.indexOf(prevProps) > -1 || rightChain.indexOf(nextProps) > -1'); false を返します。 } // 次のプロパティオブジェクトを走査し、等しくないケースを優先的に比較します for (p in nextProps) { if (nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)) { console.log('nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)'); false を返します。 } そうでない場合 (typeof nextProps[p] !== typeof prevProps[p]) { console.log('typeof nextProps[p] !== typeof prevProps[p]'); false を返します。 } } // console.log('p in prevProps'); // 前のプロパティオブジェクトを走査し、等しくないケースを優先的に比較します for (p in prevProps) { // 特定のプロパティ値が存在するかどうか if (nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)) { console.log('nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)'); false を返します。 } // プロパティ値の型は等しいか else if (typeof nextProps[p] !== typeof prevProps[p]) { console.log('typeof nextProps[p] !== typeof prevProps[p]'); false を返します。 } console.log('typeof prevProps[p]', typeof prevProps[p]); スイッチ (typeof prevProps[p]) { // オブジェクト型と関数型の処理 case 'object': ケース '関数': leftChain.push(前のプロパティ); rightChain.push(次のProps); if (!compare2Objects(前のProps[p]、次のProps[p]、左チェーン、右チェーン)) { console.log('!compare2Objects(prevProps[p], nextProps[p], leftChain, rightChain)'); false を返します。 } 左チェーン.pop(); 右チェーン.pop(); 壊す; デフォルト: // 基本的な型処理 if (prevProps[p] !== nextProps[p]) { false を返します。 } 壊す; } } true を返します。 } 3. コンポーネントメソッドをuseCallbackでラップする親コンポーネントが子コンポーネントにメソッドを渡す場合、memo は効果がないようです。const で定義されたメソッドでも、矢印関数でも、bind で定義されたメソッドでも、子コンポーネントはそれを実行します。 React をインポートし、{useState,memo} を 'react' から取得します。 //子コンポーネントの不要なレンダリングの例 interface ChildProps { 名前を変更します: ()=>void; } const FunChild = ({ changeName}: ChildProps): JSX.Element => { console.log('通常の関数サブコンポーネント') 戻る( <> <div>私は通常の関数サブコンポーネントです</div> <button onClick={changeName}>通常の関数サブコンポーネント ボタン</button> </> ); } FunMemo は、FunChild クラスのインスタンスです。 const ArrowChild = ({ changeName}: ChildProps): JSX.Element => { console.log('矢印関数のサブコンポーネント') 戻る( <> <div>私は矢印関数のサブコンポーネントです</div> <button onClick={changeName.bind(null,'test')}>矢印関数サブコンポーネント ボタン</button> </> ); } 定数 ArrowMemo = memo(ArrowChild); const BindChild = ({ changeName}: ChildProps): JSX.Element => { console.log('バインド関数サブコンポーネント') 戻る( <> <div>私はBind関数のサブコンポーネントです</div> <button onClick={changeName}>バインド関数サブコンポーネント ボタン</button> </> ); } const BindMemo = memo(BindChild); const Page = (props:any) => { 定数[count, setCount] = useState(0); const name = "テスト"; 定数changeName = 関数() { console.log('サブコンポーネントに渡されるメソッドをテストします。useCallback を使用した後も、サブコンポーネントは無効にレンダリングされますか?'); } 戻る ( <> <button onClick={(e) => { setCount(count+1) }}>1 追加</button> <p>カウント:{count} <ArrowMemo changeName={()=>changeName()}/> <BindMemo changeName={changeName.bind(null)}/> <FunMemo changeName={changeName} /> </> ) } デフォルトページをエクスポートします。 useCallbackをパラメータ[]とともに使用し、ページが最初にレンダリングされた後にcountの値を変更すると、通常の関数を渡すサブコンポーネントはレンダリングされなくなりますが、矢印関数とbindで記述されたメソッドを渡すサブコンポーネントは引き続きレンダリングされます。 React をインポートし、{useState、memo、useCallback} を 'react' からインポートします。 //子コンポーネントの不要なレンダリングの例 interface ChildProps { 名前を変更します: ()=>void; } const FunChild = ({ changeName}: ChildProps): JSX.Element => { console.log('通常の関数サブコンポーネント') 戻る( <> <div>私は通常の関数サブコンポーネントです</div> <button onClick={changeName}>通常の関数サブコンポーネント ボタン</button> </> ); } FunMemo は、FunChild クラスのインスタンスです。 const ArrowChild = ({ changeName}: ChildProps): JSX.Element => { console.log('矢印関数のサブコンポーネント') 戻る( <> <div>私は矢印関数のサブコンポーネントです</div> <button onClick={changeName.bind(null,'test')}>矢印関数サブコンポーネント ボタン</button> </> ); } 定数 ArrowMemo = memo(ArrowChild); const BindChild = ({ changeName}: ChildProps): JSX.Element => { console.log('バインド関数サブコンポーネント') 戻る( <> <div>私はBind関数のサブコンポーネントです</div> <button onClick={changeName}>バインド関数サブコンポーネント ボタン</button> </> ); } const BindMemo = memo(BindChild); const Page = (props:any) => { 定数[count, setCount] = useState(0); const name = "テスト"; 定数changeName = useCallback(() => { console.log('サブコンポーネントに渡されるメソッドをテストします。useCallback を使用した後も、サブコンポーネントは無効にレンダリングされますか?'); },[]) 戻る ( <> <button onClick={(e) => { setCount(count+1) }}>1 追加</button> <p>カウント:{count} <ArrowMemo changeName={()=>changeName()}/> <BindMemo changeName={changeName.bind(null)}/> <FunMemo changeName={changeName} /> </> ) } デフォルトページをエクスポートします。 4. useMemoを使用してコンポーネント内のオブジェクト変数をラップする子コンポーネントが memo と useCallback を使用すると、子コンポーネントにオブジェクト プロパティが渡されます。オブジェクトの値とメソッドが変更されていない場合、親コンポーネントの状態の変化に関係なく、子コンポーネントは再レンダリングされます。 React をインポートし、{useState、memo、useCallback} を 'react' からインポートします。 //子コンポーネントの不要なレンダリングの例 - memo および useCallback を使用するときに子コンポーネントにオブジェクト プロパティ値を渡す interface ChildProps { 子スタイル: { 色: 文字列; フォントサイズ: 文字列;}; 名前を変更します: ()=>void; } const FunChild = ({childStyle,changeName}: ChildProps): JSX.Element => { console.log('通常の関数サブコンポーネント') 戻る( <> <div style={childStyle}>私は通常の関数の子コンポーネントです</div> <button onClick={changeName}>通常の関数サブコンポーネント ボタン</button> </> ); } FunMemo は、FunChild クラスのインスタンスです。 const Page = (props:any) => { 定数[count, setCount] = useState(0); 定数childStyle = {色:'緑'、フォントサイズ:'16px'}; 定数changeName = useCallback(() => { console.log('サブコンポーネントに渡されるメソッドをテストします。useCallback を使用した後も、サブコンポーネントは無効にレンダリングされますか?'); },[]) 戻る ( <> <button onClick={(e) => { setCount(count+1) }}>1 追加</button> <p>カウント:{count} <FunMemo childStyle={childStyle} changeName={changeName} /> </> ) } デフォルトページをエクスポートします。 useMemo を使用すると、オブジェクトのプロパティを子コンポーネントに渡すときに不要な更新が発生する問題を解決できます。 'react' から React、{useState、memo、useMemo、useCallback} をインポートします。 //子コンポーネントの不要なレンダリングの例 interface ChildProps { 子スタイル: { 色: 文字列; フォントサイズ: 文字列;}; 名前を変更します: ()=>void; } const FunChild = ({childStyle,changeName}: ChildProps): JSX.Element => { console.log('通常の関数サブコンポーネント') 戻る( <> <div style={childStyle}>私は通常の関数の子コンポーネントです</div> <button onClick={changeName}>通常の関数サブコンポーネント ボタン</button> </> ); } FunMemo は、FunChild クラスのインスタンスです。 const Page = (props:any) => { 定数[count, setCount] = useState(0); const [名前、setName] = useState(""); 定数childStyle = {色:'緑'、フォントサイズ:'16px'}; 定数changeName = useCallback(() => { setName('名前を変更する') }, []) 定数childStyleMemo = useMemo(() => { 戻る { color: name === '名前を変更する' ? 'red':'green', フォントサイズ: '16px' } }、 [名前]) 戻る ( <> <button onClick={(e) => { setCount(count+1) }}>1 追加</button> <p>カウント:{count} <FunMemo childStyle={childStyleMemo} changeName={changeName} /> </> ) } デフォルトページをエクスポートします。 以上がReact Hooks使用回避ガイドの詳しい内容です。React Hooksの使用に関する詳しい情報は、123WORDPRESS.COM内の他の関連記事にも注目してください! 以下もご興味があるかもしれません:
|
<<: MySQL インデックスの種類 (通常、ユニーク、フルテキスト) の説明
>>: Docker で Portainer ビジュアル インターフェースを構築するための詳細な手順
1. HTMLとは何かHTML (ハイパーテキスト マークアップ言語): ハイパーテキスト マーク...
以前、「Web ページにシステムに組み込まれていないフォントを埋め込む」という研究をしたことがありま...
mysql コマンドを使用して MySQL サーバーに接続します。 MySQL サーバーが起動したら...
ファイルの保存とアクセスを容易にするために、FTPサービスが特別に構築されています。 FTP サーバ...
目次ケーススタディアカウント残高を更新する直接更新楽観的ロック方式ロックフリーソリューションキューイ...
最近、データライフサイクル管理の詳細を整理していたときに、小さな問題を発見しました。それは、MySQ...
1. 何ですか マークアップ言語として、CSSは比較的シンプルな構文とユーザーに対する要件が低いが、...
1.コアファイルプログラム実行中にセグメンテーション エラー (コア ダンプ) が発生すると、プログ...
データのバックアップと復元パート3の詳細は次のとおりです基本的な概念:バックアップ、現在のデータまた...
起動時に Ubuntu デュアル システムが停止する問題の解決方法 (Ubuntu 16.04 およ...
序文MySQL が SQL SELECT コマンドと WHERE 句を使用してテーブルからデータを読...
centos8 ディストリビューションは、BaseOS および AppStream リポジトリを通じ...
MySQL では、ほとんどのインデックス (PRIMARY KEY、UNIQUE、INDEX、FUL...
RDF と OWL は、2 つの重要なセマンティック ウェブ テクノロジーです。 RDF と OWL...
ドキュメント: https://github.com/hilongjw/vue-lazyload 1...