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

Alternative: useReducer + useContext #3

Open
wants to merge 2 commits into
base: master
from

Conversation

Projects
None yet
4 participants
@gaearon
Copy link
Contributor

gaearon commented Mar 3, 2019

This is meant to demonstrate an alternative to #2 which might make sense for larger apps. It helps you avoid the need for passing callbacks altogether which alleviates most of these concerns. The reducer form is also more flexible because you can use other state variables, or even props (by putting reducer itself inline into the component).

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

@ryardley

This comment has been minimized.

Copy link
Owner

ryardley commented Mar 3, 2019

Cool I might hold off on merging this and incorporate it into a repo that clearly shows alternatives and metrics and write an accompanying article.

@gaearon

This comment has been minimized.

Copy link
Contributor Author

gaearon commented Mar 3, 2019

The general recommendation btw is that you can keep using callbacks closer to leaf components that don’t contain a lot of computation. Such as a Button in a real app (not this one which is artificially expensive).

But that when you need to tie some application logic together that might go through many levels, that’s when you use some dispatch context. This neatly avoids having to change a dozen components each time you need a new “event” emitted from a child that affects grandparent state.

So there will be boundaries where your code will look like

const dispatch = useContext(TodosContext)

<Button onClick={() =>
  dispatch({
    type: 'add_todo',
    text
  })
>

The leaf components are fully controlled by props, but components that tie into app logic require a context above them. Note this doesn’t make them “untestable” etc. You can always put the same context above them in tests, but provide a different implementation for it.

export default memo(() => {
const dispatch = useContext(AppContext);

const handleClick = useCallback(() => {

This comment has been minimized.

@apenab

apenab Mar 4, 2019

I do not understand the approach to using useCallback if dispatch does not change.

This comment has been minimized.

@AugustinLF

AugustinLF Mar 4, 2019

Because here you're not creating a callback for dispatch but for () => { dispatch('toggle'); }, which would be recreated every time.

This comment has been minimized.

@gaearon

gaearon Mar 4, 2019

Author Contributor

Yep. That optimization isn't that important here (certainly less important than passing dispatch itself down). Just a bonus.

This comment has been minimized.

@apenab

apenab Mar 4, 2019

OK OK. As a bonus, it's fine, since I do not think it's going to make a big improvement in performance in this specific case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.