-1

I've got function component with displayProfileID function inside:

const [comments, setComments] = useState([]);
const [triggerUpdate, setTriggerUpdate] = useState(Math.random());


const displayProfileID = id => {
...
...

  try {
    axios.get(url).then(resp => {
      if (resp.data.status === "success") {
      ...
      ...
      const obj = resp.data.comments; // <-- receiving an array of objects
      setComments(obj);  // <-- changing state value
      setTriggerUpdate(Math.random()); // <-- forcing update of state variables
      console.log(comments);  //  <-- change isn't reflected ???
    }
  });
} catch (e) {
  console.log(e);
}

};

This is the only function I'm having issue with. State variable isn't changed despite triggering component rerender with Math.random().

Thank you for any pointers.

Mark
  • 1,385
  • 3
  • 16
  • 29
  • 3
    Does this answer your question? [setState doesn't update the state immediately](https://stackoverflow.com/questions/41278385/setstate-doesnt-update-the-state-immediately) – samuei Apr 27 '21 at 19:39
  • `setTriggerUpdate(Math.random());` Pointless!!! – Keith Apr 27 '21 at 19:42
  • @Keith Yes it is. Interestingly it helps in some cases. Not this one though. – Mark Apr 27 '21 at 19:44
  • What are you attempting to do with `setTriggerUpdate`? Calling `setComments` should trigger a re-render. I think the problem is you've created a race condition and `console.log` is executing before the state is updated. – C. Slack Apr 27 '21 at 19:45
  • @samuei Even though that solution may work for some. There's a good chance it will break some day again down the line. If conditions of my function change that solution will break the app. – Mark Apr 27 '21 at 19:51
  • Inside your `axios.get`, just use `obj`. The reason it's not updating is because `comments` is a closure, so it's not going to update until next render. – Keith Apr 27 '21 at 19:56
  • @Keith I'm already calling `setComments(obj);` unless you have something else in mind. – Mark Apr 27 '21 at 19:57
  • @Keith Do you mean capture response from the request call like this `const obj = axios.get();` ? – Mark Apr 27 '21 at 19:58
  • `console.log(obj)` is the same, inside you `then` callback use this instead of comments, comments is not going to update until next render. – Keith Apr 27 '21 at 19:58
  • To make things seem more consistent, I would also do -> `const comments = resp.data.comments; setComments(comments);` This way, comments will always be what your expecting, no matter if your in your async callback or not. Scope will handle the correct comments instance. – Keith Apr 27 '21 at 20:02
  • @Keith Yes, calling `console.log(resp.data.comments);` gives me the array but obviously it's only printed out in the console. It's not updated in the state (yet). It appears the problem is that I'm not using async function. I don't know how to call async function with a `id` parameter. I don't think I can do `const displayProfileID = (async e, id) => {` – Mark Apr 27 '21 at 20:07
  • 2
    It has updated the state, it's just you won't see it updated in the `comments`, because comments on the current render is a closure, it's only the next render the comments var will have the new value. When you use async in react, you have a double render, the first render will be your default value, the second render will be what you set via setState. You will see this if say you put a `console.log(comments)` after your `useState`, you will first see an empty array, and then after you axios get, you will see your returned array. During this first render, `comments` will always = `[]` – Keith Apr 27 '21 at 20:12
  • So using `async` won't help because that's useful only when I can't wait for response and my app needs to carry on (and let response to catch up in callback). So rerendering is what I need to do. – Mark Apr 27 '21 at 20:16
  • 1
    There is nothing wrong in using `async`, you just have to understand how React does it's rendering. When you call setState (setComments), React places this new value on the component state, and then performs a re-Render, it won't change the value of `comments` in the current render, as this has already been got, but comments will update on the next render that's coming. Maybe the question is, what problem are you trying solve, as this might be an XY problem. – Keith Apr 27 '21 at 20:21

1 Answers1

1

setState is not synchronous. The console.log is logging the value before it has changed (or, more accurately, it has not, and will not change, you'll get a new one—possibly—next render).

If you are rendering the content then it should update after you call that setState. The setTrigger things seems unnecessary.

When you say "I've got function in my component", where? This is a function component? Hooks work in function components, not class ones. So hopefully this code is in a functional component.

Matt Styles
  • 2,442
  • 1
  • 18
  • 23