フックを学習しようとしていますが、useState
メソッドを使用すると混乱します。配列の形で状態に初期値を割り当てています。 useState
のsetメソッドは、spread(...)
または_without spread operator
_を使用しても機能しません。状態に設定したいデータを呼び出してフェッチする別のPCでAPIを作成しました。
これが私のコードです:
_import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const StateSelector = () => {
const initialValue = [
{
category: "",
photo: "",
description: "",
id: 0,
name: "",
rating: 0
}
];
const [movies, setMovies] = useState(initialValue);
useEffect(() => {
(async function() {
try {
//const response = await fetch(
//`http://192.168.1.164:5000/movies/display`
//);
//const json = await response.json();
//const result = json.data.result;
const result = [
{
category: "cat1",
description: "desc1",
id: "1546514491119",
name: "randomname2",
photo: null,
rating: "3"
},
{
category: "cat2",
description: "desc1",
id: "1546837819818",
name: "randomname1",
rating: "5"
}
];
console.log(result);
setMovies(result);
console.log(movies);
} catch (e) {
console.error(e);
}
})();
}, []);
return <p>hello</p>;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<StateSelector />, rootElement);
_
setMovies(result)
およびsetMovies(...result)
が機能していません。ここでいくつかの助けを使うことができます。
結果変数がmovies配列にプッシュされることを期待しています。
_React.Component
_または_React.PureComponent
_を拡張して作成されたクラスコンポーネントのsetStateと同様に、useState
フックによって提供されるアップデーターを使用した状態の更新も非同期であり、すぐには反映されず更新されますがトリガーされます再レンダリング
_setMovies(result);
console.log(movies) // movies here will not be updated
_
状態の更新時にアクションを実行する場合は、クラスコンポーネントでcomponentDidUpdate
を使用する場合と同様に、useEffectフックを使用する必要があります。useStateによって返されるセッターにはコールバックパターンがないためです。
_useEffect(() => {
// action on update of movies
}, [movies]);
_
状態を更新する構文に関する限り、setMovies(result)
は、状態内の以前のmovies
値を非同期リクエストから利用可能な値に置き換えます
ただし、応答を既存の値とマージする場合は、次のようなスプレッド構文を正しく使用するとともに、状態更新のコールバック構文を使用する必要があります。
_setMovies(prevMovies => ([...prevMovies, ...result]));
_
ReactのsetState
is非同期(クラスとフックの両方)であり、観測された動作を説明するためにその事実を使用するのは魅力的ですが、_ではありません理由発生します。
TLDR:理由は、不変のconst
値の周りの closure スコープです。
(ネストされた関数内ではなく)render関数で値を読み取ります。
_useEffect(() => { setMovies(result) }, [])
console.log(movies)
_
変数を依存関係に追加します(そして react-hooks/exhaustive-deps eslintルールを使用します):
_useEffect(() => { setMovies(result) }, [])
useEffect(() => { console.log(movies) }, [movies])
_
可変参照を使用します(上記が不可能な場合):
_const moviesRef = useRef(initialValue)
useEffect(() => {
moviesRef.current = result
console.log(moviesRef.current)
}, [])
_
非同期が唯一の理由である場合、await setState()
が可能です。
ちなみに、props
とstate
はどちらも 1回のレンダリング中に不変であると仮定されています 。
_
this.state
_を不変であるかのように扱います。
フックでは、const
キーワードで定数値を使用することにより、この仮定が強化されます。
_const [state, setState] = useState('initial')
_
値は2つのレンダー間で異なる可能性がありますが、レンダー自体およびすべての closures 内で一定のままです(レンダーが終了した後でも長く存続する関数、たとえばuseEffect
、イベントハンドラー、PromiseまたはsetTimeout内)。
次の偽物を検討してくださいが、synchronous、Reactのような実装:
_// sync implementation:
let internalState
let renderAgain
const setState = (updateFn) => {
internalState = updateFn(internalState)
renderAgain()
}
const useState = (defaultState) => {
if (!internalState) {
internalState = defaultState
}
return [internalState, setState]
}
const render = (component, node) => {
const {html, handleClick} = component()
node.innerHTML = html
renderAgain = () => render(component, node)
return handleClick
}
// test:
const MyComponent = () => {
const [x, setX] = useState(1)
console.log('in render:', x) // ✅
const handleClick = () => {
setX(current => current + 1)
console.log('in handler/effect/Promise/setTimeout:', x) // ❌ NOT updated
}
return {
html: `<button>${x}</button>`,
handleClick
}
}
const triggerClick = render(MyComponent, document.getElementById('root'))
triggerClick()
triggerClick()
triggerClick()
_
_<div id="root"></div>
_