简体   繁体   中英

Updating State In React With React Hooks

I've been having a difficult time updating state inside of my React application lately using the useState hook.

If I define my state in a provider as -

const [session, setSession] = useState({});
const [sessionId, setSessionId] = useState(0);

And then try to set it using the setSession

setSession(response.data);

It always comes back as the default value. This all happens inside of the provider component - ie I'm trying to access the information within other functions in that same provider.

However, if I store that data in localStorage, for example, I have no issues accessing it whatsoever.

localStorage.setItem("session", JSON.stringify(response.data));

I've verified that the information coming from the server is an object, and that the correct data. There's no errors or promises, just the object containing the response. If I put the snippet the setSession(response.data) and localStorage.setItem("session", JSON.stringify(response.data)) next to each other, the setSession leaves the session value as {} whereas setting the local storage works perfectly. Both are using the same API response, same data


// This is the method on my component that I'm trying to use to update the state
const updateStateAfterSessionInitialization = async data => {
    setSession(data)
    localStorage.setItem("session", JSON.stringify(data));
    setSessionId(data.id);

    // both of these log a value of `{}` and `0` despite the values being set above
    console.log(session)
    console.log(sessionId)
    closeStartSessionModal();

    // If I redirect my application like this, it works fine. The ID is the correct value being returned by the server
    window.location = "/#/reading-sessions/" + data.id;
}

// All of this code below is wrapped in a function. None of this code is being executed at the top level
let response = await axios({
    method: method,
    data:data,
    url: url,
    headers: headers
});

await updateStateAfterSessionInitialization(response.data);

Literally all of the data is working perfectly fine. The server responds with the correct data, the correct data is stored the session in local storage. If I redirect using the ID from the object from the server, it works fine. But if I try to update the state of the component and access the state properly, it just just doesn't work, and as a result I'm having to try to find ways of working around setting the state.

Is there something that I'm misunderstanding here?

The code that I'm working with is here - https://github.com/aaronsnig501/decyphr-ui/commit/ef04d27c4da88cd909ce38f53bbc1babcc3908cb#diff-25d902c24283ab8cfbac54dfa101ad31

Thanks

The misunderstanding you have here is an assumption that state updates will reflect immediately which is incorrect

State update is async and will only refect in the next render cycle. If you try to update state and log it in the next line, you wouldn't see and updated state

// This is the method on my component that I'm trying to use to update the state
const updateStateAfterSessionInitialization = async data => {
    setSession(data)
    localStorage.setItem("session", JSON.stringify(data));
    setSessionId(data.id);

    // both of these log a value of `{}` and `0` despite the values being set above
    console.log(session) // This is expected to log previous value
    console.log(sessionId) // This is expected to log previous value
    closeStartSessionModal();

    window.location = "/#/reading-sessions/" + data.id;
}

Now localStorage is synchronous and hence its update is reflected immediately

If you wish to see if the update to state was done correctly you could write a useEffect that depends on it

useEffect(() => {
    console.log('State session/sessionId updated');
}, [session, sessionId])

Now depending on what you are trying to achieve you would need to modify your code in line with the above statement that state update calls are asynchronous.

Also setSession doesn't return a promise so you can't just use async await with it. You need to make use of useEffect to take an action on state update

For Example:-

import React, { useState, useEffect } from "react";
import axios from "axios";

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios("https://jsonplaceholder.typicode.com/posts");
      console.log(result.data);
      setData(result.data);
    };
    fetchData();
  }, []);

  return (
    <ul>
      {data.map(res => (
        <li>{res.title}</li>
      ))}
    </ul>
  );
}

export default App;

Check your type of response.data and defined the types array - [] objects - {} string - "" number - 0 in useState

setState is async, so new value will apply on the next rerender not in the next line.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM