Asking
Asking

Reputation: 4200

State is not set after useEffect in react js

I have the next component in my react application:

import React, { useEffect, useState } from "react";
import AsyncSelect from "react-select/async";
import { ColourOption, colourOptions } from "./docs/data";

const App = () => {
  const [state, setState] = useState([]);

  useEffect(() => {
    setState(colourOptions);
  }, []);

  return (
    <AsyncSelect
      cacheOptions
      defaultOptions
      loadOptions={async () => new Promise((r) => r(state))}
    />
  );
};

export default App;

There i set colourOptions in useEffect and after that i read the state in loadOptions. Now the options don't appear when i open the select, but when i change loadOptions={async () => new Promise((r) => r(state))} to loadOptions={async () => new Promise((r) => r(colourOptions))} the dropdown items appear.
How to make my code workable in the first situation with state, using useEffect?
demo: https://codesandbox.io/s/codesandboxer-example-forked-b3857?file=/example.tsx:355-409

Upvotes: 1

Views: 809

Answers (3)

Chirag Borawake
Chirag Borawake

Reputation: 55

First of all, the state is asynchronous it means you can not get its value immediately after you set the state.

So this problem can be fixed by initializing state.

const [state, setState] = useState(colourOptions);

Upvotes: 0

Kirubel Eneyew
Kirubel Eneyew

Reputation: 116

Since the state setting is asynchronous you can try adding a loading screen while the options state remains undefined,something like

return (
{state?
    <AsyncSelect
      cacheOptions
      defaultOptions
      loadOptions={async () => new Promise((r) => r(state))}
    />
:
<SomeLoadingScreen/>
}
  );

Upvotes: -1

Brendan Bond
Brendan Bond

Reputation: 1890

The thing is, state is set after the useEffect call (to test this, just map the state in the JSX and log each array index). The loadOptions function in react-select's async component appears to be memoized with only its props as dependencies, so you're not going to get a new loadOptions function just because you've set the state in your useEffect. In other words, upon a new render of your component the options will stay the same even though state has been updated. The question is, why use useEffect at all?

It seems to me like you're calling useEffect in your example to simulate an asynchronous call, like you'd do when you want to fetch some data. Normally, when people want to fetch data, they'll throw the async call into a useEffect and fetch the data after the first render. The thing is, the loadOptions function exposed by react-select's async component already provides this functionality - that is, it will fetch your options for you asynchronously at first blush right when you need them, rather than wait an extra step for a state update that may force you to programmatically re-instantiate the loadOptions function (by changing a prop like defaultOptions or something possibly even more hacky.

TLDR just use the loadOptions function as your asynchronous data fetcher rather than wait for useEffect.

Upvotes: 4

Related Questions