facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
228.69k stars 46.81k forks source link

useState hook not updating (onPanResponderRelease) #15291

Closed English3000 closed 5 years ago

English3000 commented 5 years ago

Do you want to request a feature or report a bug? Bug.

What is the current behavior? image

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem:

What is the expected behavior? onBoard state hook updates value.

Logic works w/o hooks: image

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

gaearon commented 5 years ago

Function components capture values they rendered with. So this behavior is expected.

Depending on what you want to do, there are a few workarounds — but usually there's also a way to structure your code in a clearer way.

https://reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function

https://overreacted.io/how-are-function-components-different-from-classes/

English3000 commented 5 years ago

I see.

To summarize how to debug when defining a function within an SFC (stateless functional component) while using Hooks:


My issues are now fixed! Thank you for the very helpful links!!

FYI: Did you see my proposal for ReactDOM.hydrateElement/2?

nerami commented 5 years ago

Thanks for the posts, they were really enlightening!

I like the suggestion of using useReducer and pass the dispatch to a provider in order to use it in child components. I wonder if creating a custom hook that provides both state and actions, and pass them to the provider, would be a nice approach. Something like:

function useTodosReducer(reducer) {
  const [data, dispatch] = useReducer(reducer)

  function addTodo(text) {
    dispatch({ type: 'add', text })
  }

  return {
    data,
    addTodo
  }
}

function TodosApp() {
  const store = useTodosReducer(todosReducer)

  return (
    <TodosDispatch.Provider value={store}>
      <DeepTree todos={store.data} />
    </TodosDispatch.Provider>
  )
}

I'm gonna try it during the weekend.

nramirez-narvar commented 5 years ago

I forgot to put here my findings on the useReducer hook.

After using hooks for a while now, I've found out that for the useReducer I'd prefer to create 2 contexts, one for the state and another one for the dispatch function.

const TodosState = createContext()
const TodosDispatch = createContext()

function TodosProvider({ children }) {
  const [state, dispatch] = useReducer(todosReducer)

  return (
    <TodosState.Provider value={state}>
      <TodosDispatch.Provider value={dispatch}>
        {children}
      </TodosDispatch.Provider>
    </TodosState.Provider>
  )
}

function useTodosState() {
  return useContext(TodosState)
}

function useTodosDispatch() {
  return useContext(TodosDispatch)
}

Having different contexts allows me to use the dispatch in components that doesn't need to re-render after the state changes.

I've also found pretty nice to implement the actions in the provider. So, instead of passing a dispatch to the context, I pass an actions API:

const TodosActions = createContext()

function TodosProvider({ children }) {
  const [state, dispatch] = useReducer(todosReducer)

  const actions = useMemo(() => {
    function addTodo(text) {
      dispatch({ type: 'add', text })
    }

    return { addTodo }
  }, [dispatch])

  return (
    <TodosState.Provider value={state}>
      <TodosActions.Provider value={actions}>
        {children}
      </TodosActions.Provider>
    </TodosState.Provider>
  )
}

function useTodosActions() {
  return useContext(TodosActions)
}