フックを使用し
ていくつかのコンポーネントを作成しました。 おそらく-彼らも小さなアプリケーションを作成しました。 一般に、結果に非常に満足しています。 あなたはAPIに慣れており、作業中にいくつかの非自明な有用なトリックを見つけました。
独自のフックをいくつか作成し、コードを300行に減らして、プログラムフラグメントの繰り返しによって以前に表されていたものをそれらに配置しました。 あなたがしたことは、同僚に見せました。 「よくやった」彼らはあなたのプロジェクトについて言った。

ただし、
useEffectを使用すると、ソフトウェアメカニズムのコンポーネントがうまく合わない場合があります。 あなたには何かが欠けているようです。 これらはすべて、クラスベースのコンポーネントライフイベントでの作業に似ていますが、本当にそうですか?
何が自分に合わないかを理解しようとすると、次の質問をしていることに気づきます。
useEffectを使用してuseEffectを再生する方法は?useEffect内にデータをロードする方法は? []とは何ですか?- 関数を効果の依存関係として指定する必要がありますか?
- プログラムがデータをリロードする無限ループに陥ることがあるのはなぜですか?
- なぜ古い状態が時々エフェクト内に見えるのか、古いプロパティが見つかるのですか?
私が最初にフックを使い始めたとき、これらの質問も私を苦しめました。 ドキュメントを準備していても、微妙な点を完全に知っているとは言えませんでした。 それ以来、突然、重要な何かに気付いた瞬間、「エウレカ!」と叫びたくなりました。 これらの瞬間に気づいたことについて、お話ししたいと思います。
useEffectについて学んだことにより、上記の質問に対する明白な回答を明確に見ることができるようになりました。
しかし、これらの質問に対する答えを見るためには、まず一歩後退する必要があります。 この記事の目的は、読者に
useEffectを
useEffectするための段階的な指示を提供することではありません。 彼らが言うように、
useEffectを「
useEffect 」するのを助けることを目指しています。 そして、率直に言って、学ぶことはあまりありません。 実際、ほとんどの時間は以前の知識を忘れることに費やします。
コンポーネントベースのコンポーネントのライフサイクルのよく知られた方法のプリズムを通して
useEffectフックを見るのをやめてから、頭の中のすべてが一緒になりました。
「教えられたことを忘れなければならない」
habr.com/ru/company/ruvds/blog/445276/Yodaこの資料の読者は
useEffect APIにある程度精通していることを
前提として
います。 これはかなり長い記事であり、小さな本と比較することができます。 実際、私は自分の考えをこのように表現することを好みます。 以下に、非常に簡潔に、上記で議論されたこれらの質問に対する答えが与えられます。 おそらく、それらはすべての資料を読む時間や欲求がない人々にとって有用です。
useEffectすべての説明と例が考慮されている形式があまり適していない場合は、これらの説明が他の無数のマニュアルに登場する瞬間まで少し待つことができます。 これは、Reactライブラリ自体と同じ話で、2013年にはまったく新しいものでした。 開発コミュニティが新しいメンタルモデルを認識し、このモデルに基づいた教材が表示されるまでには時間がかかります。
質問への回答
以下は、この資料全体の内容を読みたくない人を対象とした、この資料の冒頭で提示された質問に対する短い回答です。 これらの答えを読んでいるときに、読んでいるものの意味を本当に理解していないと感じたら、資料に目を通してください。 テキストに詳細な説明があります。 すべてを読む場合は、このセクションをスキップできます。
use useEffectを使用してcomponentDidMountを再生する方法
useEffect(fn, [])コンストラクトを使用して
componentDidMount機能を再生でき
componentDidMount 、これは
componentDidMountまったく同じではありません。 つまり、
componentDidMountとは異なり、プロパティと状態をキャプチャします。 したがって、コールバック内でも、元のプロパティと状態が表示されます。 何かの最新バージョンを見たい場合は、
refリンクにそれを書くことができます。 ただし、通常はコードを構造化するより簡単な方法があるため、これはオプションです。 メンタルエフェクトモデルは、
componentDidMountおよびその他のコンポーネントライフサイクルメソッドに適用される
componentDidMountとは異なることに注意してください。 したがって、正確な同等物を見つけようとすると、良いことよりも害になることがあります。 生産的に働くためには、いわば「効果を考える」必要があります。 メンタルモデルの基礎は、コンポーネントのライフサイクルのイベントへの応答よりも同期の実装に近いものです。
useuseEffect内にデータを適切にロードする方法 []とは何ですか?
useEffectを使用したデータのロードに関する優れたガイドを
useEffectます。 全体を読んでみてください! それほど大きくはありません。 空の配列を表す角かっこ
[]は、エフェクトがReactデータストリームに含まれる値を使用しないことを意味します。このため、その単一の使用は安全と見なすことができます。 さらに、依存関係の空の配列の使用は、特定の値が実際にエフェクトで使用される場合のエラーの一般的な原因です。 この依存関係を不当に破棄するのではなく、依存関係の必要性を排除するのに役立ついくつかの戦略(主に
useReducerと
useCallback形式で提示)を習得する必要があります。
functions効果の依存関係として関数を指定する必要がありますか?
プロパティや状態を必要としない関数は、コンポーネントの外部で使用することをお勧めします。また、エフェクトでのみ使用される関数は、エフェクト内に配置することをお勧めします。 その後、エフェクトがレンダーのスコープ内にある関数(プロパティからの関数を含む)を
useCallback使用する場合は、それらを宣言されている
useCallbackでラップし、再度使用してみてください。 なぜこれが重要なのですか? 関数は、プロパティと状態から値を「見る」ことができるため、データストリームに参加します。 これについての詳細は、FAQをご覧ください。
▍プログラムがデータのリロードの無限ループに陥ることがあるのはなぜですか?
これは、依存関係を表す2番目の引数を持たないエフェクトでデータの読み込みが実行されるときに発生する可能性があります。 これがない場合、各レンダリング操作の後にエフェクトが実行されます。つまり、状態を設定すると、そのようなエフェクトが呼び出されます。 常に変化する値が依存関係配列に示されている場合も、無限ループが発生する可能性があります。 依存関係を一度に1つずつ削除することで、どのような値が可能なのかを調べてください。 ただし、依存関係を削除する(または
[]を使用して突発的に行う)ことは、通常、問題を解決するための間違ったアプローチです。 代わりに、問題の原因を見つけて実際に解決する必要があります。 たとえば、関数は同様の問題を引き起こす可能性があります。 それらをエフェクトに入れたり、コンポーネントの外側に移動したり、
useCallbackラップしたりすることで
useCallbackます。 複数のオブジェクトを作成しないようにするには、
useMemoを使用できます。
▍古い状態がエフェクト内に表示されたり、古いプロパティが見つかったりするのはなぜですか?
エフェクトは、宣言されたレンダーから常にプロパティと状態を「参照」します。 これは
エラーの防止に役立ちますが、場合によってはコンポーネントの通常の動作を妨げる可能性があります。 このような場合、可変
refリンクを明示的に使用して、そのような値を操作できます(これについては、前述の記事の最後で参照できます)。 古いレンダリングからプロパティまたは状態が表示されると思っているが、これを期待していない場合は、いくつかの依存関係を見逃している可能性があります。 それらを見ることを学ぶために、リンターの
このルールを使用してください。 数日で、それはあなたの第二の自然のようなものになります。 また、よくある質問で
この回答をご覧ください。
質問に対するこれらの回答が、それらを読んだ人に役立つことを願っています。 では
useEffectについて
useEffectましょう。
各レンダーには、独自のプロパティと状態があります。
効果について説明する前に、レンダリングについて説明する必要があります。
機能的なカウンターコンポーネントは次のとおりです。
function Counter() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
<p>You clicked {count} times</p> 。 彼女はどういう意味ですか? 定数定数は何らかの形で状態の変化を「観察」し、自動的に更新されますか? この結論は、Reactを研究している人の一種の貴重な最初のアイデアと考えることができますが
、何が起こっている
のかを
正確に理解する
精神的なモデルではありません。
この例では、
countは単なる数字です。 これは、ある種の魔法の「データバインディング」ではなく、ある種の「オブザーバーオブジェクト」や「プロキシ」などではありません。 私たちの前に、このような古い良い数字があります:
const count = 42;
最初のコンポーネントの出力中、
useState()から取得した
countは0です
setCount(1)を呼び出すと、Reactはコンポーネントを再度呼び出します。 この時間
countは1になります。
Reactは、状態を更新するたびにコンポーネントを呼び出します。 結果として、各レンダリング操作は、関数内で定数である
counter状態の独自の値を「認識」します。
その結果、この行は特別なデータバインディング操作を実行しません。
<p>You clicked {count} times</p>
レンダリング中に生成されたコードに数値のみを埋め込みます。 この番号はReactによって提供されます。
setCountを呼び出すと、Reactは異なる
count値でコンポーネントを再度呼び出します。 次に、ReactはDOMを更新して、コンポーネントレンダリング中にドキュメントオブジェクトモデルが最新のデータ出力と一致するようにします。
これから引き出すことができる最も重要な結論は、
countは特定のレンダー内では定数であり、時間とともに変化しないということです。 何度も呼び出されるコンポーネントが変更されます。 各レンダリングは独自の
count値を「認識」します。この
count値は、レンダリング操作ごとに分離されます。
この資料には、このプロセスに関する詳細が記載されています。
各レンダーには、独自のイベントハンドラがあります。
すべてはまだ明確です。 イベントハンドラはどうですか?
この例を見てください。 ここでは、ボタンをクリックしてから3秒後に、
count保存されている値に関する情報を含むメッセージボックスが表示され
count 。
function Counter() { const [count, setCount] = useState(0); function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + count); }, 3000); } return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> <button onClick={handleAlertClick}> Show alert </button> </div> ); }
次の一連のアクションを実行するとします。
Click me ]ボタンをClick me 、 countを3にします。- [
Show alert表示]ボタンをクリックします。 - タイムアウトが期限切れになる前に、値を5に増やします。
[アラートを表示]ボタンをクリックした後、カウント値を増やすメッセージボックスに何が表示されると思いますか? タイマーがトリガーされたときの
count値に対応する5がそこに表示されますか、3-ボタンが押された瞬間の
count値ですか?
これで、この質問に対する答えが見つかりますが、すべてを自分で調べたい場合
は、この例の作業バージョンがあります。
あなたが見たものがあなたにとって理解できないように思えるなら、現実に近い例がここにあります。 状態で、メッセージの現在の受信者の
IDが保存され、
Sendボタンがあるチャットアプリケーションを想像して
Send 。
この資料では、何が起こっているのかを詳細に検討します。 実際、メッセージボックスに表示される内容の質問に対する正しい答えは3です。
メッセージボックスを表示するメカニズムは、ボタンがクリックされた瞬間の状態を「キャプチャ」しました。
動作の別のバリアントを実装する方法がありますが、ここではシステムの標準的な動作を扱います。 テクノロジーのメンタルモデルを構築する場合、「最小の抵抗のパス」をあらゆる種類の「緊急出口」と区別することが重要です。
これはどのように機能しますか?
countの値は、関数の特定の呼び出しごとに定数であると既に述べました。 これについて詳しく説明する価値があると思います。 ポイントは、関数が何度も呼び出されることです(レンダリング操作ごとに1回)が、これらの各呼び出しでは、その内部の
countは定数です。 この定数は、特定の値(特定のレンダリング操作の状態を表す)に設定されます。
この関数の振る舞いはReactにとって特別なものではありません-通常の関数は同様に振る舞います:
function sayHi(person) { const name = person.name; setTimeout(() => { alert('Hello, ' + name); }, 3000); } let someone = {name: 'Dan'}; sayHi(someone); someone = {name: 'Yuzhi'}; sayHi(someone); someone = {name: 'Dominic'}; sayHi(someone);
この例では、外部変数
someone数回再割り当てされます。 同じことがReact内のどこかで発生する可能性があり、コンポーネントの現在の状態が変わる場合があります。 ただし、
sayHi関数の内部には、特定の呼び出しの
person関連付けられたローカル定数
nameがあります。 この定数はローカルであるため、異なる関数呼び出しの値は互いに分離されています! その結果、タイムアウト後、表示された各メッセージウィンドウは独自の
name値を「記憶」します。
これは、ボタンがクリックされたときにイベントハンドラーが
count値をキャプチャする方法を説明しています。 コンポーネントを使用して同じ原則を適用すると、各レンダリングが独自の
count値を「見る」ことがわかります。
その結果、各レンダーは実際に独自の「バージョン」
handleAlertClick返します。 これらの各バージョンは、独自の
count値を「記憶」しています。
そのため、
この例では、イベントハンドラーは特定のレンダリングに「属し」、ボタンをクリックすると、コンポーネントはこれらのレンダリングの
count状態を使用し
count 。
特定の各レンダリング内で、プロパティと状態は常に同じままです。 ただし、異なるレンダリング操作が独自のプロパティと状態を使用する場合、それらを使用するメカニズム(イベントハンドラーを含む)でも同じことが起こります。 また、特定のレンダリングに「所属」します。 したがって、イベントハンドラー内の非同期関数でさえ、同じ
count値を「参照」します。
上記の例では、特定の
count値を
handleAlertClick関数に直接埋め込みました。
count定数は特定のレンダリング内で変更できないため、この「メンタル」置換は私たちを傷つけません。 第一に、それは定数であり、第二に、それは数値です。 オブジェクトなどの他の意味について考えることもできますが、これは、状態の変更(突然変異)を行わないことを規則として受け入れる場合に限ります。 同時に、既存のオブジェクトを変更するのではなく、新しいオブジェクトで
setSomething(newObj)を呼び出すことに満足しています。このアプローチでは、前のレンダリングに属する状態は変更されないためです。
各レンダリングには独自の効果があります。
ご存知のように、この資料はエフェクトに専念していますが、それらについてはまだ話していません。 今それを修正します。 結局のところ、エフェクトの操作は、既にわかっているものと特に違いはありません。
ドキュメントの
例を考えてみましょう。これは、すでに分析したものと非常に似ています。
function Counter() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
今、あなたに質問があります。 エフェクトは最新の
count値をどのように読み取りますか?
おそらく、ある種の「データバインディング」がここで使用されているのでしょうか、それともエフェクト関数内の
count値を更新する「オブザーバーオブジェクト」ですか? 多分
countは、コンポーネント内でReactが値を設定する可変変数であり、その結果、エフェクトは常に最新バージョンを認識しますか?
いや
特定のコンポーネントのレンダリングでは、
countが定数であることをすでに知っています。
countは特定のスコープにある定数であるため、イベントハンドラーでさえ、「属する」レンダーから
count値を「参照」し
count 。 同じことがエフェクトにも当てはまります!
また、これは変数
countはなく、「変更されていない」エフェクト内で何らかの形で変化することに注意してください。 エフェクト関数自体は、レンダリング操作ごとに異なります。
各バージョンは、「属する」レンダーの
count値を「認識」し
count 。
React , DOM .
, ( ), , , , «» , «».
, , .
, ( ,
). , , , .
, , :
React:
:
- :
<p>You clicked 0 times</p> . - , , :
() => { document.title = 'You clicked 0 times' } .
React:
ブラウザ:
React:
- , , .
() => { document.title = 'You clicked 0 times' } .
, . , , - :
:
React:
:
- :
<p>You clicked 1 times</p> . - , , :
() => { document.title = 'You clicked 1 times' } .
React:
ブラウザ:
React:
- , , .
() => { document.title = 'You clicked 1 times' } .
…
, , , , «» .
. 次のコードを検討してください。
function Counter() { const [count, setCount] = useState(0); useEffect(() => { setTimeout(() => { console.log(`You clicked ${count} times`); }, 3000); }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
, ?
, . , , , . しかし、これはそうではありません! , , , , ,
count .
.
: «, ! ?».
, ,
this.setState , , . , ,
, , , :
componentDidUpdate() { setTimeout(() => { console.log(`You clicked ${this.state.count} times`); }, 3000); }
,
this.state.count count , , . , , , 5 , 5 .
, JavaScript-, , ,
, ,
setTimeout , . , (React
this.state , ), .
— , , «» , . , , , . , , . , , , , , ,
.
, ( , , - API ) , .
:
function Example(props) { useEffect(() => { setTimeout(() => { console.log(props.counter); }, 1000); });
, «» . ! . , .
, , - , , , , . ,
ref ,
.
, , , , , . , ( ), «» React-. , , . , .
, , , :
function Example() { const [count, setCount] = useState(0); const latestCount = useRef(count); useEffect(() => {
- React . React
this.state . , ,
latestCount.current . , . , , , .
?
, . , , «» .
次のコードを検討してください。
useEffect(() => { ChatAPI.subscribeToFriendStatus(props.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.id, handleStatusChange); }; });
,
props —
{id: 10} ,
{id: 20} — . , :
- React
{id: 10} . - React
{id: 20} . - React
{id: 20} .
( , , .)
, «» - , , «» - , . — , , , . .
React
, . , . . . :
- React
{id: 20} . - .
{id: 20} . - React
{id: 10} . - React
{id: 20} .
, «»
props ,
{id: 10} , ,
props {id: 20} .
, …
— ?: « ( , , - API ) , ».
! « » , . , , :
, , … , «» , -,
{id: 10} .
React . , , .
props , .
,
React , .
.
, :
function Greeting({ name }) { return ( <h1 className="Greeting"> Hello, {name} </h1> ); }
,
<Greeting name="Dan" /> , —
<Greeting name="Yuzhi" /> ,
<Greeting name="Yuzhi" /> .
Hello, Yuzhi .
, , . React, . , , .
$.addClass $.removeClass jQuery- ( — , «»), , CSS- React ( — , «»).
React DOM , . «» «».
.
useEffect , React, .
function Greeting({ name }) { useEffect(() => { document.title = 'Hello, ' + name; }); return ( <h1 className="Greeting"> Hello, {name} </h1> ); }
useEffect , , . , - , ! , «», «».
,
A ,
B , —
C , ,
C . (, - ), .
, , , . ( ).
これに対処する方法は?
React
React DOM. DOM , React DOM, - .
, :
<h1 className="Greeting"> Hello, Dan </h1>
:
<h1 className="Greeting"> Hello, Yuzhi </h1>
React :
const oldProps = {className: 'Greeting', children: 'Hello, Dan'}; const newProps = {className: 'Greeting', children: 'Hello, Yuzhi'};
React ,
children , DOM. ,
className . :
domNode.innerText = 'Hello, Yuzhi';
- ? , , .
, , - :
function Greeting({ name }) { const [counter, setCounter] = useState(0); useEffect(() => { document.title = 'Hello, ' + name; }); return ( <h1 className="Greeting"> Hello, {name} <button onClick={() => setCounter(counter + 1)}> Increment </button> </h1> ); }
counter .
document.title name ,
name .
document.title counter , .
React … ?
let oldEffect = () => { document.title = 'Hello, Dan'; }; let newEffect = () => { document.title = 'Hello, Dan'; };
そうでもない。 React , , . ( .
name .)
, , (
deps ),
useEffect :
useEffect(() => { document.title = 'Hello, ' + name; }, [name]);
, React: «, , , ,
name ».
, , React :
const oldEffect = () => { document.title = 'Hello, Dan'; }; const oldDeps = ['Dan']; const newEffect = () => { document.title = 'Hello, Dan'; }; const newDeps = ['Dan'];
, , ! - - .
React
React — . , , , ,
useEffect , , , . ( !)
function SearchResults() { async function fetchData() {
FAQ , . .
« !», — . : , , . , , , — , .
, , . , , , , . , . .
, , .
, React
, , React , .
useEffect(() => { document.title = 'Hello, ' + name; }, [name]);
—, , ,
[] , , , , :
useEffect(() => { document.title = 'Hello, ' + name; }, []);
—. , «» , , .
, , , . , : «
setInterval clearInterval ».
. , , ,
useEffect , , ,
[] . - , ?
function Counter() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, []); return <h1>{count}</h1>; }
, ,
.
, « , », . , , ,
setInterval , . , ?
, — React , , . ,
count , React , , , . — .
count 0.
setCount(count + 1) setCount(0 + 1) . , —
[] ,
setCount(0 + 1) :
React, , , — .
count — , ( ):
const count =
. React .
,. , , React , , . —
- .
React , . , , , .
, , , .
count :
useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]);
. , , — , .
count ,
count ,
setCount(count + 1) :
,
setInterval ,
count , . , .
,, , , . — , .
.
,
count .
useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]);
, ,
count . ,
count setCount . , ,
count . , ,
setState :
useEffect(() => { const id = setInterval(() => { setCount(c => c + 1); }, 1000); return () => clearInterval(id); }, []);
« ». ,
count - ,
setCount(count + 1) .
count - ,
count + 1 «» React. React
count . , React — , , , .
setCount(c => c + 1) . « React », , . « » , ,
.
, , , . React.
count :
,.
,
setInterval , ,
c => c + 1 .
count . React .
Google Docs
, , — ? , , «», , . , Google Docs . . , .
, . . ,
setCount(c => c + 1) , ,
setCount(count + 1) , «»
count . , ( — «»). « React» —
. .
( ) , Google Docs
. — , React . , , ( , , ) .
,
setCount(c => c + 1) , . , . , , , , , .
setCount(c => c + 1) .
useReducer .
, :
count step .
setInterval ,
step :
function Counter() { const [count, setCount] = useState(0); const [step, setStep] = useState(1); useEffect(() => { const id = setInterval(() => { setCount(c => c + step); }, 1000); return () => clearInterval(id); }, [step]); return ( <> <h1>{count}</h1> <input value={step} onChange={e => setStep(Number(e.target.value))} /> </> ); }
.
, React .
step , . .
:
step setInterval —
step . , , , ! , , , , , .
, , ,
setInterval ,
step .
step ?
, ,
useReducer .
,
setSomething(something => ...) , , . «», , , .
step dispatch :
const [state, dispatch] = useReducer(reducer, initialState); const { count, step } = state; useEffect(() => { const id = setInterval(() => { dispatch({ type: 'tick' });
.
: « , ?». , React ,
dispatch . .
!
(
dispatch setstate useRef , React , . — .)
, , , , .
step . , . , . :
const initialState = { count: 0, step: 1, }; function reducer(state, action) { const { count, step } = state; if (action.type === 'tick') { return { count: count + step, step }; } else if (action.type === 'step') { return { count, step: action.step }; } else { throw new Error(); } }
, , , .
useReducer — -
, , , . , , ? , , API
<Counter step={1} /> . ,
props.step ?
, ! , :
function Counter({ step }) { const [count, dispatch] = useReducer(reducer, 0); function reducer(state, action) { if (action.type === 'tick') { return state + step; } else { throw new Error(); } } useEffect(() => { const id = setInterval(() => { dispatch({ type: 'tick' }); }, 1000); return () => clearInterval(id); }, [dispatch]); return <h1>{count}</h1>; }
, . , , , , .
.
dispatch . , , . .
, , . «» , , ? ,
dispatch , React . . .
useReducer «-» . , . , , , , .
, - , .
, , , :
function SearchResults() { const [data, setData] = useState({ hits: [] }); async function fetchData() { const result = await axios( 'https://hn.algolia.com/api/v1/search?query=react', ); setData(result.data); } useEffect(() => { fetchData(); }, []);
, .
, , . , , , , , , , , .
, , , , :
function SearchResults() {
, , :
function SearchResults() { const [query, setQuery] = useState('react');
, (, ), . .
, . , :
function SearchResults() {
.
? , « ». React, - .
getFetchUrl ,
query , , , , . — ,
query :
function SearchResults() { const [query, setQuery] = useState('react'); useEffect(() => { function getFetchUrl() { return 'https://hn.algolia.com/api/v1/search?query=' + query; } async function fetchData() { const result = await axios(getFetchUrl()); setData(result.data); } fetchData(); }, [query]);
.
, « React».
query . , , , , . , , .
exhaustive-deps eslint-plugin-react-hooks , . , , .
.
, ?
. , , . , , .
? , . : React . . , « ». , , . , , , !
, , . ,
getFetchUrl :
function SearchResults() { function getFetchUrl(query) { return 'https://hn.algolia.com/api/v1/search?query=' + query; } useEffect(() => { const url = getFetchUrl('react');
getFetchUrl — , .
, «» , .
getFetchUrl (, , ), :
function SearchResults() {
,
getFetchUrl . , — . - , , . , , , .
— .
, , :
, . , , .
. ,
useCallback :
function SearchResults() {
useCallback . : , -, , , .
, . (
'react' 'redux' ). , , ,
query . , ,
query ,
getFetchUrl .
,
query useCallback :
function SearchResults() { const [query, setQuery] = useState('react'); const getFetchUrl = useCallback(() => {
useCallback query , ,
getFetchUrl ,
query :
function SearchResults() { const [query, setQuery] = useState('react');
useCallback ,
query ,
getFetchUrl , , .
query ,
getFetchUrl , . Excel: - , , , .
— , . , :
function Parent() { const [query, setQuery] = useState('react');
fetchData Parent query ,
Child , .
?
, , , , . , , , , :
class Parent extends Component { state = { query: 'react' }; fetchData = () => { const url = 'https://hn.algolia.com/api/v1/search?query=' + this.state.query;
, : « , , ,
useEffect —
componentDidMount componentDidUpdate . !».
componentDidUpdate :
class Child extends Component { state = { data: null }; componentDidMount() { this.props.fetchData(); } componentDidUpdate(prevProps) {
,
fetchData — ! (, , , .) - , .
this.props.fetchData prevProps.fetchData . , , ?
componentDidUpdate(prevProps) { this.props.fetchData(); }
. . ( .) ,
fetchData this.state.query ?
render() { return <Child fetchData={this.fetchData.bind(this, this.state.query)} />; }
this.props.fetchData !== prevProps.fetchData true , ,
query ! .
, , ,
query Child . , ,
query ,
query :
class Parent extends Component { state = { query: 'react' }; fetchData = () => { const url = 'https://hn.algolia.com/api/v1/search?query=' + this.state.query;
, , - , , .
, , .
this , . , , , , - . ,
this.props.fetchData , , , , , .
-
useCallback . , , , . , .
useCallback props.fetchData .
,
useMemo :
function ColorPicker() {
,
useCallback , - . « », , , . , . ,
.
,
fetchData ( ), . , , . («
props.onComplete , ?») , .
, :
class Article extends Component { state = { article: null }; componentDidMount() { this.fetchData(this.props.id); } async fetchData(id) { const article = await API.fetchArticle(id); this.setState({ article }); }
, , , . . — , :
class Article extends Component { state = { article: null }; componentDidMount() { this.fetchData(this.props.id); } componentDidUpdate(prevProps) { if (prevProps.id !== this.props.id) { this.fetchData(this.props.id); } } async fetchData(id) { const article = await API.fetchArticle(id); this.setState({ article }); }
, , , . , . ,
{id: 10} ,
{id: 20} , , . , , , . .
, , . — , ,
async/await ( , - ) , ( , ).
,
async -. (, , , , .)
, , ! .
, :
function Article({ id }) { const [article, setArticle] = useState(null); useEffect(() => { let didCancel = false; async function fetchData() { const article = await API.fetchArticle(id); if (!didCancel) { setArticle(article); } } fetchData(); return () => { didCancel = true; }; }, [id]);
, , , . , .
, , , , , . , , , . . — .
useEffect , , , . React. ,
useEffect .
, , « », . . , , , , «» , .
,
useEffect , . — . — , , — , . , , , , API.
, ,
useFetch , ,
useTheme , . , ,
useEffect . , , , .
, ,
useEffect . — , . , . ?
Suspense React , , - ( : , , ) .
Suspense , ,
useEffect , , , - . , , , . , ,
, , .
まとめ
, . , , - , , , .
