1. ホーム
  2. ジャバスクリプト

[解決済み】useState setメソッドが変更を即座に反映しない。

2022-03-23 06:11:13

質問

フックを勉強しようとしているのですが useState メソッドで混乱しました。私は配列の形でステートに初期値を代入しています。のsetメソッドは useState は、スプレッドシンタックスを使用した場合と使用しない場合の両方で、私には機能しません。

別のPCでAPIを作り、それを呼び出して状態に設定したいデータを取ってきています。

以下は私のコードです。

<div id="root"></div>

<script type="text/babel" defer>
// import React, { useState, useEffect } from "react";
// import ReactDOM from "react-dom";
const { useState, useEffect } = React; // web-browser variant

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 =", result);
        setMovies(result);
        console.log("movies =", movies);
      } catch (e) {
        console.error(e);
      }
    })();
  }, []);

  return <p>hello</p>;
};

const rootElement = document.getElementById("root");
ReactDOM.render(<StateSelector />, rootElement);
</script>

<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

どちらも setMovies(result) また setMovies(...result) が動作します。

を期待しています。 result 変数に押し込まれます。 movies の配列になります。

どのように解決するのですか?

を拡張して作成されたクラスコンポーネントのsetStateとよく似ています。 React.Component または React.PureComponent で提供されるアップデータを用いて、状態の更新を行う。 useState フックも非同期であり、すぐには反映されない。

また、ここでの主な問題は、非同期であることだけではなく、状態の値が現在のクロージャに基づいて関数で使用され、状態の更新が次の再レンダリングに反映されることで、既存のクロージャは影響を受けず、新しいクロージャが作成されるということです。 . 現在の状態では、フック内の値は既存のクロージャによって取得され、再レンダリングが起こったときに、関数が再び再作成されるかどうかに基づいてクロージャが更新されます。

を追加しても setTimeout 関数では、再レンダリングが行われるであろう時間後にタイムアウトが実行されますが setTimeout は、更新された値ではなく、以前のクロージャの値をまだ使用します。

setMovies(result);
console.log(movies) // movies here will not be updated

状態の更新時にアクションを実行したい場合は、useEffectフックを使用する必要があります。 componentDidUpdate useState が返すセッターはコールバックパターンを持たないため、クラスコンポーネントで使用されます。

useEffect(() => {
    // action on update of movies
}, [movies]);

状態を更新する構文としては setMovies(result) は、以前の movies の値は、非同期リクエストから利用可能なものに変更されます。

しかし、レスポンスを以前に存在した値とマージしたい場合は、以下のようなスプレッド構文を正しく使用するとともに、状態更新のコールバック構文を使用する必要があります。

setMovies(prevMovies => ([...prevMovies, ...result]));