1. ホーム
  2. javascript

[解決済み】コンポーネント内でuseStateからstate updaterを複数回呼び出すと、何度も再レンダリングされる。

2022-04-12 03:18:23

質問

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つの呼び出しを行いますが、quot;hooks way"では状態をより小さなユニットに分割しているようです。これを軽減する方法について何かアイデアはありますか?

解決方法は?

を組み合わせることができます。 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>