Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How use async return value from useEffect as default value in useState?

I've created a simple example https://codesandbox.io/s/4zq852m7j0.

As you can see I'm fetching some data from a remote source. I'd like to use the return value as the value inside my textfield.

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;
};

However using the value inside the useState function does not work. I think useState uses the default value only on first render. When first rendering the value is obviously not set since it's async. The textfield should have the value bar but it is empty.

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>
  );
}

After fetching the value from remote I'd like to be able to change it locally.

Any ideas?

like image 605
zemirco Avatar asked Nov 14 '18 10:11

zemirco


People also ask

Can I use async function in useEffect?

Either way, we're now safe to use async functions inside useEffect hooks. Now if/when you want to return a cleanup function, it will get called and we also keep useEffect nice and clean and free from race conditions. Enjoy using async functions with React's useEffect from here on out!

Can I return a value from useEffect?

you can return your value and put in state and use it outside useEffect. if your value need preState, remember to pass an arrow function in setState and pass preState as a parameter.

How do you set initial value in useState?

To set a conditional initial value for useState in React:Pass a function to the useState hook. Use a condition to determine the correct initial value for the state variable. The function will only be invoked on the initial render.

Is set state hook async?

This function is used to update the state of a component, but it's important to remember that setState is asynchronous.


2 Answers

Now that useFetch returns a value that is available asynchronously, what you need is to update localState when the remoteValue is available, for that you can write an effect

const remoteName = useFetch();
  // i want to see the remote value inside my textfield
  const [name, setName] = useState(remoteName);
  useEffect(
    () => {
      console.log("inside effect");
      setName(remoteName);
    },
    [remoteName] // run when remoteName changes
  );

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

Working demo

like image 64
Shubham Khatri Avatar answered Oct 19 '22 03:10

Shubham Khatri


This is exactly same case as setting initial state asynchronously in class component:

state = {};

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

Asynchronously retrieved state cannot be available during initial render. Function component should use same technique as class component, i.e. conditionally render children that depend on a state:

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

This way there's no reason for useFetch to have its own state, it can maintain common state with the component (an example):

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>
  );
}
like image 22
Estus Flask Avatar answered Oct 19 '22 03:10

Estus Flask