初めて React フックを試していますが、データを取得して 2 つの異なる状態変数 (データと読み込みフラグ) を更新すると、状態アップデーターへの両方の呼び出しが同じ関数内で発生しているにもかかわらず、コンポーネント (データ テーブル) が 2 回レンダリングされることに気付くまではすべて順調に見えました。以下は、両方の変数をコンポーネントに返す API 関数です。
const getData = url => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(async () => {
const test = await api.get('/people')
if(test.ok){
setLoading(false);
setData(test.data.results);
}
}, []);
return { data, loading };
};
通常のクラス コンポーネントでは、複雑なオブジェクトになる可能性のある状態を更新するために 1 回の呼び出しを行いますが、「フックの方法」では状態を小さな単位に分割するようです。その副作用として、個別に更新すると複数回の再レンダリングが行われるようです。これを軽減するアイデアはありますか?
ベストアンサー1
loading
状態とdata
状態を 1 つの状態オブジェクトに結合し、1 回のsetState
呼び出しでレンダリングが 1 回だけ行われるようにすることができます。
注:クラス内のコンポーネントとは異なりsetState
、setState
から返される はuseState
オブジェクトを既存の状態とマージせず、オブジェクト全体を置き換えます。マージを行う場合は、以前の状態を読み取り、新しい値とマージする必要があります。ドキュメントを参照してください。
パフォーマンスに問題があると判断されるまで、レンダリングを過度に呼び出すことについてはあまり心配しないでください。レンダリング (React コンテキスト内) と仮想 DOM 更新を実際の DOM にコミットすることは別の問題です。ここでのレンダリングは仮想 DOM の生成を指し、ブラウザー DOM の更新を指しているわけではありません。React は呼び出しをバッチ処理しsetState
、最終的な新しい状態でブラウザー DOM を更新する場合があります。
const {useState, useEffect} = React;
function App() {
const [userRequest, setUserRequest] = useState({
loading: false,
user: null,
});
useEffect(() => {
// Note that this replaces the entire object and deletes user key!
setUserRequest({ loading: true });
fetch('https://randomuser.me/api/')
.then(results => results.json())
.then(data => {
setUserRequest({
loading: false,
user: data.results[0],
});
});
}, []);
const { loading, user } = userRequest;
return (
<div>
{loading && 'Loading...'}
{user && user.name.first}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>
代替案 - 独自の状態マージフックを作成する
const {useState, useEffect} = React;
function useMergeState(initialState) {
const [state, setState] = useState(initialState);
const setMergedState = newState =>
setState(prevState => Object.assign({}, prevState, newState)
);
return [state, setMergedState];
}
function App() {
const [userRequest, setUserRequest] = useMergeState({
loading: false,
user: null,
});
useEffect(() => {
setUserRequest({ loading: true });
fetch('https://randomuser.me/api/')
.then(results => results.json())
.then(data => {
setUserRequest({
loading: false,
user: data.results[0],
});
});
}, []);
const { loading, user } = userRequest;
return (
<div>
{loading && 'Loading...'}
{user && user.name.first}
</div>
);
}
ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>