Home > database >  How to get updated data in a setTimeout from redux saga?
How to get updated data in a setTimeout from redux saga?

Time:12-08

I am using Redux Saga along with Redux Toolkit. I have dispatched an action and need its result in a setTimeout. But once I have dispatched the action the code execution continues and when the setTimeout runs, I do not get the updated result instead I get the results from previously dispatched action (I dispatch the same action when the component mounts).

File.js

const {neededData} = useSelector(state => state.something) // property from redux store

useEffect(() => {
  dispatch(Slice.actions.getHealthDataBetweenDates(params));
}, [])

function someFunction() {

  dispatch(Slice.actions.getHealthDataBetweenDates(params));
  console.log(neededData) // logs an array of objects, but the it is not updated data

  setTimeout(() => {
    console.log(neededData) // logs an array of objects, but it is not updated data
  }, 1000)
}

Saga.js

function* getHealthDataBetweenDates({ payload }) {
    try {
        const result = yield call(ProfileApi.getHealthDataBetweenDates, payload)

        if (result.status == true) {
            yield put(Slice.actions.getHealthDataBetweenDatesSuccess(result.data));
            console.log('saga minutes', result.data.data) // returns array of object, with updated data
        }else {
            yield put(Slice.actions.getHealthDataBetweenDatesFailure());
        }
    } catch (error) {
        console.log('error: '   error)
    }
}

When the function executes, I first get the log right below the dispatch statement, then the log from saga and then the log from setTimeout.

CodePudding user response:

When you run setTimeout, you create a closure around the function you pass in. Variables are "frozen" at the time the closure is created. React will still re-render your component when your saga updates your store, but the values in setTimeout have already been closed. The MDN spec on closures is well-written and very helpful.

React offers an "escape hatch" from these restrictions: refs. Refs exist outside the render cycle of the component and its closures. You can wire a ref to your store with useEffect to get the behavior you're looking for.

Here's a minimal example:

  // let's say the selector returns 0
  const data = useSelector(state => state.data);
  const ref = useRef(data);
  
  // update the ref every time your selector changes
  useEffect(() => {
    ref.current = data;
  }, [data]);

  // start timeouts
  useEffect(() => {
    // set data at 500ms
    setTimeout(() => dispatch(Slice.actions.setData(1)), 500);
    // log data at 1000ms
    setTimeout(() => console.log({ data: ref.current }), 1000);
  }, []);

This will log { data: 1 } to the console after ~1000ms.

  • Related