ただし、
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; // ... <p>You clicked {count} times</p> // ...
最初のコンポーネントの出力中、
useState()
から取得した
count
は0です
setCount(1)
を呼び出すと、Reactはコンポーネントを再度呼び出します。 この時間
count
は1になります。
// function Counter() { const count = 0; // useState() // ... <p>You clicked {count} times</p> // ... } // function Counter() { const count = 1; // useState() // ... <p>You clicked {count} times</p> // ... } // function Counter() { const count = 2; // useState() // ... <p>You clicked {count} times</p> // ... }
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
値を「見る」ことがわかります。
// function Counter() { const count = 0; // useState() // ... function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + count); }, 3000); } // ... } // function Counter() { const count = 1; // useState() // ... function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + count); }, 3000); } // ... } // function Counter() { const count = 2; // useState() // ... function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + count); }, 3000); } // ... }
その結果、各レンダーは実際に独自の「バージョン」
handleAlertClick
返します。 これらの各バージョンは、独自の
count
値を「記憶」しています。
// function Counter() { // ... function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + 0); }, 3000); } // ... <button onClick={handleAlertClick} /> // , 0 // ... } // function Counter() { // ... function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + 1); }, 3000); } // ... <button onClick={handleAlertClick} /> // , 1 // ... } // function Counter() { // ... function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + 2); }, 3000); } // ... <button onClick={handleAlertClick} /> // , 2 // ... }
そのため、 この例では、イベントハンドラーは特定のレンダリングに「属し」、ボタンをクリックすると、コンポーネントはこれらのレンダリングの
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
。
// function Counter() { // ... useEffect( // () => { document.title = `You clicked ${0} times`; } ); // ... } // function Counter() { // ... useEffect( // () => { document.title = `You clicked ${1} times`; } ); // ... } // function Counter() { // ... useEffect( // () => { document.title = `You clicked ${2} times`; } ); // .. }
React , DOM .
, ( ), , , , «» , «».
, , .
, ( , ). , , , .
, , :
React:
- , 0.
:
- :
<p>You clicked 0 times</p>
. - , , :
() => { document.title = 'You clicked 0 times' }
.
React:
- もちろん。 . , , - DOM.
ブラウザ:
- , .
React:
- , , .
-
() => { document.title = 'You clicked 0 times' }
.
, . , , - :
:
- , React, 1.
React:
- , 1.
:
- :
<p>You clicked 1 times</p>
. - , , :
() => { document.title = 'You clicked 1 times' }
.
React:
- もちろん。 . , , - DOM.
ブラウザ:
- , .
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); }); // ... } function Example(props) { const counter = props.counter; useEffect(() => { setTimeout(() => { console.log(counter); }, 1000); }); // ... }
, «» . ! . , .
, , - , , , , . ,
ref
, .
, , , , , . , ( ), «» React-. , , . , .
, , , :
function Example() { const [count, setCount] = useState(0); const latestCount = useRef(count); useEffect(() => { // count latestCount.current = count; setTimeout(() => { // console.log(`You clicked ${latestCount.current} times`); }, 3000); }); // ...
- 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 ) , ».
! « » , . , , :
// , props {id: 10} function Example() { // ... useEffect( // () => { ChatAPI.subscribeToFriendStatus(10, handleStatusChange); // return () => { ChatAPI.unsubscribeFromFriendStatus(10, handleStatusChange); }; } ); // ... } // , props {id: 20} function Example() { // ... useEffect( // () => { ChatAPI.subscribeToFriendStatus(20, handleStatusChange); // return () => { ChatAPI.unsubscribeFromFriendStatus(20, handleStatusChange); }; } ); // ... }
, , … , «» , -,
{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'; // domNode.className
- ? , , .
, , - :
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 , ?
そうでもない。 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
React — . , , , ,
useEffect
, , , . ( !)
function SearchResults() { async function fetchData() { // ... } useEffect(() => { fetchData(); }, []); // ? . . // ... }
FAQ , . .
« !», — . : , , . , , , — , .
, , . , , , , . , . .
, , .
, React
, , React , .
useEffect(() => { document.title = 'Hello, ' + name; }, [name]);
—
, , ,
[]
, , , , :
useEffect(() => { document.title = 'Hello, ' + name; }, []); // : 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)
:
// , 0 function Counter() { // ... useEffect( // () => { const id = setInterval(() => { setCount(0 + 1); // setCount(1) }, 1000); return () => clearInterval(id); }, [] // ); // ... } // 1 function Counter() { // ... useEffect( // - , // React , . () => { const id = setInterval(() => { setCount(1 + 1); }, 1000); return () => clearInterval(id); }, [] ); // ... }
React, , , — .
count
— , ( ):
const count = // ... useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, []);
. React .
,
. , , React , , . — - .
React , . , , , .
, , , .
count
:
useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]);
. , , — , .
count
,
count
,
setCount(count + 1)
:
// , 0 function Counter() { // ... useEffect( // () => { const id = setInterval(() => { setCount(0 + 1); // setCount(count + 1) }, 1000); return () => clearInterval(id); }, [0] // [count] ); // ... } // , 1 function Counter() { // ... useEffect( // () => { const id = setInterval(() => { setCount(1 + 1); // setCount(count + 1) }, 1000); return () => clearInterval(id); }, [1] // [count] ); // ... }
,
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' }); // setCount(c => c + step); }, 1000); return () => clearInterval(id); }, [dispatch]);
.
: « , ?». , 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 getFetchUrl() { return 'https://hn.algolia.com/api/v1/search?query=react'; } // , async function fetchData() { const result = await axios(getFetchUrl()); setData(result.data); } useEffect(() => { fetchData(); }, []); // ... }
, , :
function SearchResults() { const [query, setQuery] = useState('react'); // , function getFetchUrl() { return 'https://hn.algolia.com/api/v1/search?query=' + query; } // , async function fetchData() { const result = await axios(getFetchUrl()); setData(result.data); } useEffect(() => { fetchData(); }, []); // ... }
, (, ), . .
, . , :
function SearchResults() { // ... useEffect(() => { // ! function getFetchUrl() { return 'https://hn.algolia.com/api/v1/search?query=react'; } async function fetchData() { const result = await axios(getFetchUrl()); setData(result.data); } fetchData(); }, []); // . // ... }
.
? , « ». 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 useEffect(() => { const url = getFetchUrl('redux'); // ... - ... }, []); // : getFetchUrl // ... }
getFetchUrl
— , .
, «» , .
getFetchUrl
(, , ), :
function SearchResults() { // function getFetchUrl(query) { return 'https://hn.algolia.com/api/v1/search?query=' + query; } useEffect(() => { const url = getFetchUrl('react'); // ... - ... }, [getFetchUrl]); // , . useEffect(() => { const url = getFetchUrl('redux'); // ... - ... }, [getFetchUrl]); // , . // ... }
,
getFetchUrl
. , — . - , , . , , , .
— .
, , :
// function getFetchUrl(query) { return 'https://hn.algolia.com/api/v1/search?query=' + query; } function SearchResults() { useEffect(() => { const url = getFetchUrl('react'); // ... - ... }, []); // . useEffect(() => { const url = getFetchUrl('redux'); // ... - ... }, []); // . // ... }
, . , , .
. , useCallback :
function SearchResults() { // , const getFetchUrl = useCallback((query) => { return 'https://hn.algolia.com/api/v1/search?query=' + query; }, []); // . useEffect(() => { const url = getFetchUrl('react'); // ... - ... }, [getFetchUrl]); // . useEffect(() => { const url = getFetchUrl('redux'); // ... - ... }, [getFetchUrl]); // // ... }
useCallback
. : , -, , , .
, . (
'react'
'redux'
). , , ,
query
. , ,
query
,
getFetchUrl
.
,
query
useCallback
:
function SearchResults() { const [query, setQuery] = useState('react'); const getFetchUrl = useCallback(() => { // query return 'https://hn.algolia.com/api/v1/search?query=' + query; }, []); // : query // ... }
useCallback
query
, ,
getFetchUrl
,
query
:
function SearchResults() { const [query, setQuery] = useState('react'); // query const getFetchUrl = useCallback(() => { return 'https://hn.algolia.com/api/v1/search?query=' + query; }, [query]); // . useEffect(() => { const url = getFetchUrl(); // ... - ... }, [getFetchUrl]); // . // ... }
useCallback
,
query
,
getFetchUrl
, , .
query
,
getFetchUrl
, . Excel: - , , , .
— , . , :
function Parent() { const [query, setQuery] = useState('react'); // query const fetchData = useCallback(() => { const url = 'https://hn.algolia.com/api/v1/search?query=' + query; // ... ... }, [query]); // return <Child fetchData={fetchData} /> } function Child({ fetchData }) { let [data, setData] = useState(null); useEffect(() => { fetchData().then(setData); }, [fetchData]); // // ... }
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; // ... - ... }; render() { return <Child fetchData={this.fetchData} />; } } class Child extends Component { state = { data: null }; componentDidMount() { this.props.fetchData(); } render() { // ... } }
, : « , , ,
useEffect
—
componentDidMount
componentDidUpdate
. !».
componentDidUpdate
:
class Child extends Component { state = { data: null }; componentDidMount() { this.props.fetchData(); } componentDidUpdate(prevProps) { // if (this.props.fetchData !== prevProps.fetchData) { this.props.fetchData(); } } render() { // ... } }
,
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; // ... - ... }; render() { return <Child fetchData={this.fetchData} query={this.state.query} />; } } class Child extends Component { state = { data: null }; componentDidMount() { this.props.fetchData(); } componentDidUpdate(prevProps) { if (this.props.query !== prevProps.query) { this.props.fetchData(); } } render() { // ... } }
, , - , , .
, , .
this
, . , , , , - . ,
this.props.fetchData
, , , , , .
-
useCallback
. , , , . , .
useCallback
props.fetchData
.
,
useMemo
:
function ColorPicker() { // Child, // . const [color, setColor] = useState('pink'); const style = useMemo(() => ({ color }), [color]); return <Child style={style} />; }
,
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
, , , - . , , , . , , , , .
まとめ
, . , , - , , , .