web-dev-qa-db-ja.com

useEffectからの非同期戻り値をuseStateのデフォルト値としてどのように使用しますか?

簡単な例を作成しました https://codesandbox.io/s/4zq852m7j

ご覧のとおり、リモートソースからいくつかのデータをフェッチしています。テキストフィールド内の値として戻り値を使用したいと思います。

const useFetch = () => {
  const [value, setValue] = useState("");

  useEffect(
    async () => {
      const response = await fetch("https://httpbin.org/get?foo=bar");
      const data = await response.json();
      setValue(data.args.foo);
    },
    [value]
  );

  return value;
};

ただし、useState関数内で値を使用することはできません。 useStateは最初のレンダリングでのみデフォルト値を使用すると思います。最初にレンダリングするとき、値は非同期であるため、明らかに設定されていません。テキストフィールドの値はbarである必要がありますが、空です。

function App() {
  const remoteName = useFetch();
  // i want to see the remote value inside my textfield
  const [name, setName] = useState(remoteName);

  const onChange = event => {
    setName(event.target.value);
  };

  return (
    <div className="App">
      <p>remote name: {remoteName}</p>
      <p>local name: {name}</p>
      <input onChange={onChange} value={name} />
    </div>
  );
}

リモートから値を取得した後、ローカルで値を変更できるようにしたいと思います。

何か案は?

6
zemirco

これは、クラスコンポーネントで非同期的に初期状態を設定する場合とまったく同じです。

state = {};

async componentDidMount() {
  const response = await fetch(...);
  ...
  this.setState(...);
}

非同期で取得された状態は、初期レンダリング中に使用できません。関数コンポーネントは、クラスコンポーネントと同じ手法を使用する必要があります。つまり、状態に依存する子を条件付きでレンダリングします。

  return name && <div className="App">...</div>;

このように、useFetchが独自の状態を持つ理由はなく、コンポーネントとの共通の状態を維持できます( ):

const useFetch = () => {
  const [value, setValue] = useState("");

  useEffect(
    async () => {
      const response = await fetch("https://httpbin.org/get?foo=bar");
      const data = await response.json();
      setValue(data.args.foo);
    },
    [] // executed on component mount
  );

  return [value, setValue];
};

function App() {
  const [name, setName] = useFetch();

  const onChange = event => {
    setName(event.target.value);
  };

  return name && (
    <div className="App">
      <p>local name: {name}</p>
      <input onChange={onChange} value={name} />
    </div>
  );
}
1
Estus Flask