How to handle stale state and get the latest value of it inside a function in react?

1.1k views Asked by At

how can I get the latest value of config in doSomething function? Do I need to put all the state values to a ref and then access it everywhere with the latest value? Is there a better/alternative way of handling this?

import React, { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { createSelector } from 'reselect'

import { saveConfiguration } from '../../store/actions'

const selectConfig = createSelector([(state) => state.config.config], (val) => val)

function Configuration() {
  const [config, setConfig] = useState({})

  const dispatch = useDispatch()

  const store = {
    configuration: useSelector(selectConfig)
  }

  function doSomething() {
    console.log(config)
    // 3. How to get the updated config value from state?
    // Even I use useStateWithCallback custom hook and then call doSomething
    // again I didn't get the updated state value of config
  }

  function updateRedux() {
    return dispatch(saveConfiguration({ person: { name: 'John', age: 30 } })) // this returns a promise
  }

  function handleClick() {
    updateRedux().then(() => {
      //1. How to get latest value of store.configuration not in then params above?
      //2. Imagine I get the latest value and update the state with it
      setConfig(store.configuration)
      doSomething()
    })
  }

  return (
    <button onClick={() => handleClick()}>Update Redux</button>
  )
}

export default Configuration
1

There are 1 answers

8
CodinCat On

Instead of calling the function after the setConfig, try useEffect:

useEffect(() => {
  // do something..
}, [config])

This effect will be triggered automatically when config changed.

Not related to the question, but in your current code I can see that you are trying to sync a configuration from the redux store to a local state. I'm not sure about your use case, but it's unnecessary in most cases. Why not just use the value from the redux store? Having a single source of truth can prevent your code from becoming buggy and unmaintainable.


A more complete code:

function externalFunction(config) {
  console.log(config) // Always the latest
}

const Configuration = () => {
  const [config, setConfig] = useState({})

  function doThis() {
    console.log(config) // Always the latest
  }
  function doThat() {
    console.log(config.person) // Always the latest
  }

  useEffect(() => {
    doThis()
    externalFunction(config)
  }, [config])

  useEffect(() => {
    doThat()
  }, [config.person])

  return null
}

The above code should work, but it's not the best practice. If you have the hooks rule in eslint you will see some warning. You can use useCallback to solve the issue:

const doThis = useCallback(() => {
  console.log(config)
}, [config])

const doThat = useCallback(() => {
  console.log(config.person)
}, [config.person])

useEffect(() => {
  doThis()
  externalFunction(config)
}, [doThis, config])

useEffect(() => {
  doThat()
}, [doThat])

A small demo: https://codesandbox.io/s/stackoverflow-useeffect-config-mn1x7v