こんにちは 9月24〜25日に、HolyJsフロントエンド開発者の会議https://holyjs-moscow.ru/がモスクワで開催されました。 私たちは、クイズを開催したスタンドで会議に来ました。 メインクイズがありました-予選4ラウンドと最終ラウンド1で、Apple WatchとLegoのコンストラクターがプレイされました。 また、個別に、反応知識クイズを実施しました。
katの下-反応時のクイズタスクの解析。 正しいオプションはネタバレの下に隠されるので、分析を読むことができるだけでなく、自分自身をチェックすることもできます:)
行こう!
便宜上、質問をセクションにグループ化しました。
セクション1. this.setStateの操作の基本的な理解とコンポーネントのライフサイクルの更新:
質問1。
react-: 1) SetProps, SetState, ForceUpdate 2) ForceUpdate, SetState 3) ForceUpdate, SetState, Parent (re)render 4) ForceUpdate, SetState, directly call UpdateComponent
3)ForceUpdate、SetState、Parent(re)render
質問2。
, this.setState({}) react 1) , updating lifecycle 2) , 3) React "Object cannot be empty" 4) state
1)コンポーネントはダーティとマークされ、更新ライフサイクルが呼び出されます
質問に答えるために、2つの部分を分析します。
1)更新サイクルに対する独自のコンポーネント要求
2)コンポーネント外のリクエスト
コンポーネント自体には、自身を更新する2つの方法があります。
1)this.setStateおよびthis.forceUpdate。 この場合、コンポーネントはダーティとしてマークされ、「調整」チェックで、レンダリングの優先順位にある場合、更新サイクルが開始されます。
興味深い事実: this.setState({})
とthis.forceUpdate
は異なります。 this.setState({})
が呼び出されると、shouldComponentUpdateメソッドなしで更新ループが開始されるthis.forceUpdate
とは異なり、完全な更新ループが呼び出されます。 this.setState({})
の例は、 https ://codesandbox.io/s/m5jz2701l9にあります (例でsetStateをforceUpdateに置き換えると、コンポーネントの動作がどのように変化するかを確認できます)。
2)コンポーネントの親が再レンダリングされると、vDOMパーツ、更新が必要なすべての子が返され、それらは完全更新ライフサイクルとも呼ばれます。 shouldComponentUpdateを記述するか、コンポーネントをPureComponentとして定義することにより、サブツリーの完全な再カウントを回避できます。
質問3
Component PureComponent (PC) 1) Component , Pure 2) PC SCU, shallowEqual props state 3) PC , store 4) PC shouldComponentUpdate
2)PCはSCUを実装し、shallowEqualプロップとステートを実行します
前に説明したように、親が(再)レンダリングすると、サブツリー全体が更新ライフサイクルに送信されます。 ルート要素を更新したと想像してください。 この場合、連鎖効果によると、反応ツリーのほぼ全体を更新する必要があります。 不要な更新を最適化して送信しないようにするには、 shouldComponentUpdate
メソッドがあり、コンポーネントを更新する必要がある場合はtrueを返し、そうでない場合はfalseを返すことができます。 reactでの比較を簡単にするために、 PureComponent
から継承してすぐに参照できるPureComponent
を取得できshouldComponentUpdate
。これは、参照(オブジェクト型について説明している場合)または値(値型について説明している場合)によって、コンポーネントに入るすべてのshouldComponentUpdate
と状態を比較します。
質問4。
this.setState(() => {}, () => {}) — setState? 1) set . updating 2) state 3) setState 1
2)2番目の関数は、状態の更新後に呼び出されます
Reactライフサイクルには、マウントループ用のcomponentDidMountと更新用のcomponentDidMount
2つのメソッドがあり、コンポーネントの更新後にロジックを追加できます。 たとえば、httpリクエストを作成し、スタイルを変更し、html要素のメトリックを取得し、(条件により)setStateを作成します。 状態の特定のフィールドを変更した後に何らかのアクションを実行する場合、 componentDidUpdate
メソッドで比較を記述する必要があります。
componentDidUpdate(prevProp, prevState) { if (prevState.foo !== this.state.foo) { // do awesome things here } }
または、setStateで実行できます。
setState( // set new foo {foo: 'baz'}, () => { // do awesome things here } );
それぞれの方法には長所と短所があります(たとえば、複数の場所でsetStateを変更する場合、条件を1回記述する方が便利な場合があります)。
質問5。
render: class A extends React.PureComponent { render() { console.log('render'); return <div /> } } function Test() { return <A foo='bar' onClick={() => console.log('foo')} /> } const rootElement = document.getElementById("root"); ReactDOM.render(<Test />, rootElement); setTimeout(() => ReactDOM.render(<Test />, rootElement)); 1) 1 2) 2 3) 3 4) 0
2)2
質問6。
render: class A extends React.PureComponent { render() { console.log('render'); return <div /> } } function Test() { return <A foo='bar' /> } const rootElement = document.getElementById("root"); ReactDOM.render(<Test />, rootElement); setTimeout(() => ReactDOM.render(<Test />, rootElement)); 1) 1 2) 2 3) 3 4) 0
1)1
質問7。
render: class A extends React.PureComponent { componentDidMount() { console.log('render'); } render() { return <div /> } } const rootElement = document.getElementById("root"); ReactDOM.render(<A />, rootElement); setTimeout(() => ReactDOM.render(<A />, rootElement)); 1) 1 2) 2 3) 3 4) 0
1)1
質問5–7同じことが必要ですPureComponent
の動作の理解と、小道具の転送時のコンポーネントの更新を確認するためです。 renderメソッド内でjsxコールバックを渡し、これをrender関数で直接記述した場合:
render () { return <Button onClick={() => {}} />; }
次に、親の各レンダリングは、このクリックハンドラーを更新します。 これは、各レンダリングが一意のリンクを持つ新しい関数を作成するために発生します。PureComponentで比較すると、新しい小道具が古い小道具と等しくないため、コンポーネントを更新する必要があります。 すべてのチェックに合格し、shouldComponentUpdateがfalseを返す場合、更新は行われません。
セクション2. Reactのキー
ここでキーの詳細な分析を公開しました: https : //habr.com/company/hh/blog/352150/
質問1。
key, ? 1) key 2) updating lifecycle 3) key 4) reconciliation
1)キーを変更するときに、以前のインスタンスを削除し、新しいインスタンスをマウントします
キーを使用しない場合、reactはペアの要素のリストを上から下へ比較します。 キーを使用すると、対応するキーで比較が行われます。 新しいキーが出現した場合、そのようなコンポーネントは誰とも比較されず、すぐにゼロから作成されます。
要素が1つでもこのメソッドを使用できます。 <A key="1" />
設定できます。次のレンダリングでは<A key="2" />
を指定します。この場合、reactは<A key="1" />
を削除し<A key="1" />
そして、最初から<A key="2" />
を作成します。
質問2。
this.prop.key? 1) 2) 3) static getKey
2)いいえ
コンポーネントは、小道具として渡された子からキーを学習できますが、キーについては学習できません。
質問3。
render: class A extends React.PureComponent { componentDidMount() { console.log('render'); } render() { return <div /> } } const rootElement = document.getElementById("root"); ReactDOM.render(<A key='1' />, rootElement); setTimeout(() => ReactDOM.render(<A />, rootElement)); 1) 1 2) 2 3) 3 4) 0
2)2
キーを変更すると、コンポーネントが再作成されるため、レンダリングが2回表示されます。
セクション3. jsxに関する質問
質問1。
. 1) prop / context 2) 3) setParentProps 4) static getParentRef
1)prop / contextの形式でのコールバック
2)モデルのレイヤーを削除し、作業を進める
正解は2つあります。 クイズでそれらのいずれかを選択すると、ポイントがカウントされます。 この質問は、データフローリアクションの知識のためのものです。 上から下へのデータは、小道具またはコンテキストの形で配布され、コールバックを含めることができます。コールバックは、以下のコンポーネントが呼び出してシステムの状態に影響を与えることができます。
モデルの削除(コンテキストとプロップ)を組み合わせた別の方法は、たとえば、react-reduxバインディングです。
このライブラリは、react(redux)から派生したモデルを使用します。 プロバイダーにredux.storeを設定します。実際にコンテキストにストアを設定します。 次に、開発者はHOC接続を使用します。これはコンテキストに入り、ストアに変更を登録(store.subscribe)し、ストアが変更されるとmapStateToProps
関数を再mapStateToProps
ます。 データが変更されている場合、小道具でそれらをラップされたオブジェクトに設定します。
同時に、connectを使用すると、開発者がコンポーネントに渡す必要のあるactionCreatorを指定するmapDispatchToProps
を指定できます。 次に、外部から(コンテキストなしで)それらを受け取り、 actionCreators
をストアにバインドし( actionCreators
でラップします)、それらを小道具としてラップされたコンポーネントに渡します。
質問2。
props jsx? 1) 2) children
1)任意の
任意に転送できます。 例:
<Button icon={<Icon kind='warning'/>}></Button>
アイコン付きのボタンを描画します。 このアプローチは、1つの子の小道具を整理するのではなく、コンポーネントにさまざまな要素の相対位置を制御する権利を残すために使用すると非常に便利です。
セクション4. setStateの高度な理解
強く関連する3つの質問を次に示します。
質問1。
this.state = {a: 'a'}; ... this.setState({a: 'b'}); this.setState({a: this.state.a + 1}) this.state? 1) {a: 'a1'} 2) {a: 'b1'} 3) 4) {a: 'a'}
3)データ不足
質問2。
this.state={a: 'a'} ... this.setState({a: 'b'}) this.setState(state => ({a: state.a + 1})) this.state? 1) {a: 'a1'} 2) {a: 'b1'} 3) 4) {a: 'ab1'}
2){a: 'b1'}
質問3。
2 setState componentDidUpdate updating lifecycle 1) 1 2) 2 3) 3 4)
1)1
setStateのすべての作業は、ここで完全に説明されています。
1) https://reactjs.org/docs/react-component.html#setstate
2) https://stackoverflow.com/questions/48563650/does-react-keep-the-order-for-state-updates/48610973#48610973
実際には、setStateは同期的に発生しません。
また、react-lifecycleメソッド、react-eventのハンドラー関数(onChange、onClick)内にあるかどうかに応じて、setStateの呼び出しが連続して複数ある場合、setStateの実行は異なります。
SetStateは、reactハンドラー内でバッチで動作します(呼び出しスタック内のユーザー定義関数が終了し、イベントハンドラーとライフサイクルメソッドを呼び出した関数に入ると、変更がロールバックされます)。 それらは次々に回転するため、反応ハンドラ内にいる場合、次のようになります。
this.state = {a: 'a'}; // a: 'a' ... this.state.a // a: 'a' this.setState({a: 'b'}); // a: 'b' + . this.state.a // a: 'a' this.setState({a: this.state.a + 1}) // a: 'a1'
変更がbatchevoで発生したため。
しかし同時に、reset-handlersの外部でsetStateが呼び出された場合:
this.state = {a: 'a'}; // a: 'a' ... this.state.a // a: 'a' this.setState({a: 'b'}); // a: 'b' + this.state.a // a: 'b' this.setState({a: this.state.a + 1}) // a: 'b1' +
この場合、変更は個別にロールされるためです。
セクション5. Redux
質問1。
action, () => {} ? 1) . action type 2) , action type 3) , middleware action 4) , dispatch
3)はい、そのようなアクションのためにカスタムミドルウェアを定義する必要があります
最も単純な例としてredux-thunkを取り上げてください。 すべてのミドルウェアは小さなコードブロックです。
https://github.com/reduxjs/redux-thunk/blob/master/src/index.js#L2-L9
return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); };
ミドルウェアはどのように機能しますか?
アクションがストアに到着する前に、コントロールを取得します。 したがって、上書きされたアクションは、最初にミドルウェアチェーンを通過します。
各ミドルウェアは、ストアインスタンス、次のメソッドを受け入れます。これにより、アクションとアクション自体をさらに転送できます。
ミドルウェアがredux-thunkなどのカスタムアクションを処理する場合、アクションが関数の場合、アクションはそれ以上転送されず、「 "れ」、代わりにそこに渡されたdispatchおよびgetStateメソッドでアクションを呼び出します。
redux-thunkが次にアクションを実行した場合、機能はどうなりますか?
レデューサーを呼び出す前に、storeはアクションのタイプをチェックします。 次の条件を満たす必要があります。
1)オブジェクトでなければなりません
2)タイプフィールドが必要です
3)型フィールドは文字列型でなければなりません
いずれかの条件が満たされない場合、reduxはエラーをスローします。
ボーナスの質問:
ボーナス質問1。
? class Todos extends React.Component { getSnapshotBeforeUpdate(prevProps, prevState) { return this.props.list.length - prevProps.list.length; } componentDidUpdate(a, b, c) { console.log(c); } ... } ReactDOM.render(<Todos list={['a','b']} />, app); setTimeout(() => ReactDOM.render(<Todos list={['a','b','a','b']} />, app), 0); a) 0 b) 1 c) 2 d) undefined
c)2
getSnapshotBeforeUpdate
は、 getSnapshotBeforeUpdate
でめったに使用されない関数であり、スナップショットを取得して、それをcomponentDidUpdateに渡すことができます。 このメソッドは、特定のデータを事前に計算し、それに基づいて、たとえばフェッチリクエストを作成するために必要です。
ボーナス質問2。
2,5 ? function Input() { const [text, setText] = useState("World!"); useEffect( () => { let id = setTimeout(() => { setText("Hello " + text); }, 1000); return () => { clearTimeout(id); }; }, [text] ); return ( <input value={text} onChange={e => { setText(e.target.value); }} /> ); } a) "World!" b) "Hello World!" c) "Hello Hello World!" d)
c)「Hello Hello World!」
これはすでに、reactの新機能を知ることの問題であり、クイズには含まれていません。 コメントを試して、最後の質問のコードの動作を詳細に説明しましょう:)