Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple calls in same rendering cycle references old state #48

Open
ypresto opened this issue Jul 24, 2019 · 3 comments
Open

Multiple calls in same rendering cycle references old state #48

ypresto opened this issue Jul 24, 2019 · 3 comments

Comments

@ypresto
Copy link

ypresto commented Jul 24, 2019

function useCounter(initialState = 0) {
  let [count, setCount] = useState(initialState)
  let decrement = () => setCount(count - 1)
  let increment = () => setCount(count + 1)
  return { count, decrement, increment }
}

then

counter.increment()
counter.increment()

=> counter.count === 1

For example, this could be caused by immediate error callback of API client.

function useStore(initialState = 0) {
  const [state, setState] = useState(initialState)
  const onInput = (input) => setState({ ...state, input })
  const onRequestStart = () => setState({ ...state, loading: true, sentInput: state.input })
  const onRequestFailed = () => setState({ ...state, loading: false })
  return { state, decrement, increment }
}

const [state, actions] = Store.useContainer()
actions.onRequestStart()
api.call(params, (data, error) => {
  if (error) actions.onRequestFailed() // could reference old state value, "sentInput" value will be lost.
})

Maybe related: #26

@Pytal
Copy link

Pytal commented Aug 1, 2019

Found a solution using useEffect.

@shrugs
Copy link

shrugs commented Sep 12, 2019

This is standard react hooks behavior — your count variable is closed-over when creating your decrement behavior. In this case you probably want to use the reducer form of setState or just use useReducer directly.

You'll also want to wrap those callbacks in useCallback to avoid unnecessary re-renders.

function useCounter(initialState = 0) {
  const [count, setCount] = useState(initialState)
  const decrement = useCallback(() => setCount(count => count + 1), [])
  const increment = useCallback(() => setCount(count => count + 1), [])

  return { count, decrement, increment }
}

@garretto
Copy link

garretto commented Oct 30, 2019

The answer by @shrugs should be added to the official documentation. I think there are also cases where decrement and increment should just have their own context, without count. That's similar to how useReducer is recommended with context.

https://reactjs.org/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants