0

I have an application using hooks using state and setState as variable names, hope that is not too confusing. I try to call setState(newState) and then run my rotate() function afterward when I check rotate it still has my slots empty. As if it did not update in time. Do I need to run this as a callback? Is setState not finished by the time setState is finished?

   const [ state, setState ] = useState({ manual: true, rings: [], slots: [] });
    const slotMap = {
      0: 288,
      1: 216,
      2: 144,
      3: 72,
      4: 360
  };

    useEffect(()=>{
      const newState = { ...state, slots: props.slots };
      console.log('DEGREES:');
      newState.slots.forEach(x => console.log(slotMap[x])); //works! 
      if(newState.slots !== undefined && props.apiHit === true ) {
        setState(newState);
        rotate();
      }

    }, [props.slots, props.apiHit]);

rotate =() => {
  console.log(state); 
}

output:

{manual: true, rings: Array(3), slots: Array(0)}manual: truerings: (3) [{…}, {…}, {…}]slots: Array(0)length: 0__proto__: Array(0)__proto__: Object

I definitely have props.slots having data. It just doesn't seem to populate when rotate is called. What am I missing in understanding about setState?

Kyle Calica-St
  • 2,629
  • 4
  • 26
  • 56
  • 1
    setting state is an assynchronous operation. Just call `console.log` in your component's body and you should see the correct updated value (in the next render) – Dupocas Nov 13 '19 at 18:42
  • 1
    or pass the updated state manually to your `rotate` function. – Emile Bergeron Nov 13 '19 at 18:43
  • 1
    That's also a great ideia. Since you already knows the next state you can just pass it to rotate – Dupocas Nov 13 '19 at 18:44
  • yes that's what I was going to do was pass it as a parameter. I was just confused on `setState`'s behavior. Thank you all for letting me know! – Kyle Calica-St Nov 13 '19 at 18:46
  • @Dupocas can I use `async/wait` or use `.then()` with `setState`? or would this be bad practice? – Kyle Calica-St Nov 13 '19 at 18:47
  • @Dupocas The setter from useState is not asynchronous but since it doesn't mutate state you can see the changes only in the next render cycle so you have a stale closure. – HMR Nov 13 '19 at 18:54
  • In class based components `setState` takes an aditional argument which is a callback function to execute when all updates are done. But in functional components you don't even need that. The updated value will be available in the next render. Period – Dupocas Nov 13 '19 at 18:54
  • 2
    All state changes are assynchronous @HMR. Since react batches the updates in a qeue you can't really depend on the updated value at runtime. The next render in functional components is only triggered when the old and new state don't match anymore triggering the reconciliation. But the process of updating the internals of react still is async. So it may feel like sync, but in reality the new render is triggered by an async operation – Dupocas Nov 13 '19 at 18:54
  • 3
    @KyleCalica-St You can't use `await setState` because the setter from useState does not return a promise. Technically it **is** asynchronous since it can do batch updates but it's not asynchronous like fetch where you get a promise. I suggest just passing newState to rotate. – HMR Nov 13 '19 at 18:59
  • 2
    Exactly! Just pass the new state to rotate – Dupocas Nov 13 '19 at 19:00
  • wow this was a very helpful discussion thanks everyone! – Kyle Calica-St Nov 13 '19 at 19:03
  • @KyleCalica-St Another way to do it is: `const rotate = ()=>setState(state=>{console.log(state);return state;})` Because you are returning the same state it would not cause a re render. However; if you want to change `state` (or any other value created with useState) then passing newState to rotate will be best. – HMR Nov 13 '19 at 19:04

0 Answers0