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

useState hook not updating (onPanResponderRelease) #15291

Closed
English3000 opened this issue Apr 1, 2019 · 4 comments
Closed

useState hook not updating (onPanResponderRelease) #15291

English3000 opened this issue Apr 1, 2019 · 4 comments

Comments

@English3000
Copy link

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:

git clone https://github.com/English3000/Elixir.git
cd Elixir/functional_design/islands
git checkout refactor/bottom-up
cd apps/islands_interface/assets && npm i
cd ../../.. && mix phx.server

Go to localhost:4000 && join a game

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?

  • React version: "^16.8.4"... then tried "^16.8.6"
  • Browser: Mozilla
  • OS: Mac Mojave
@gaearon
Copy link
Collaborator

gaearon commented Apr 3, 2019

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/

@gaearon gaearon closed this as completed Apr 3, 2019
@English3000
Copy link
Author

English3000 commented Apr 3, 2019

I see.

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

  • state ~ useRef instead of useState when you want to set a value at a different stage in the lifecycle than constructor/1
  • props w/ useEffect ~ if it remains stale, make sure you've added that specific stale prop to the dependencies array

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

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

@nerami
Copy link

nerami commented Sep 6, 2019

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
Copy link

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)
}

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