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

Provide withHooks HOC to decouple hooks and components #15392

Closed
malerba118 opened this issue Apr 11, 2019 · 3 comments
Closed

Provide withHooks HOC to decouple hooks and components #15392

malerba118 opened this issue Apr 11, 2019 · 3 comments

Comments

@malerba118
Copy link

malerba118 commented Apr 11, 2019

Do you want to request a feature or report a bug?
Feature

What is the current behavior?
Currently the recommended way to use hooks involves coupling them with components. By this I mean that components are aware of the hooks they consume and depend on them explicitly.
This issue thread on the recompose repo discusses this issue in some detail and how recompose favored keeping components dumb, but wraps them in HOC's to make them smart. Hooks promote baking the smartness right into the component itself.

What is the expected behavior?
React should offer a way to decouple components from the hooks they consume. I suggest a withHooks HOC that maps hooks to props. This will be a familiar model for those who have used redux with react.

const withHooks = mapHooksToProps => WrappedComponent => {
  return props => {
    let hookProps = mapHooksToProps(props);
    return <WrappedComponent {...hookProps} {...props} />;
  };
};

const Counter = props => {
  return (
    <div>
      <div>Counter: {props.counter}</div>
      <button onClick={props.increment}>Increment</button>
      <button onClick={props.decrement}>Decrement</button>
    </div>
  );
};

const mapHooksToProps = props => {
  let [counter, setCounter] = useState(0);

  return {
    counter,
    increment: () => setCounter(prev => prev + 1),
    decrement: () => setCounter(prev => prev - 1)
  };
};

const EnhancedCounter = withHooks(mapHooksToProps)(Counter);

Demo:
https://codesandbox.io/s/ympq0rlv79

Some reasons why this is nice:

  1. It decouples components from the things that make them smart. Some examples of things that could make dumb components smart include hooks, redux, and good ol' parent components. By mapping hooks to props, we make it very easy to swap a dumb component's hook-powered 'brain' for a new 'brain', say a redux-powered 'brain'.

  2. Not sure if it's a good idea, but i know several people trying to replace redux with hooks in their applications. For these people, migrations from redux to hooks would be dead simple because they could replace mapStateToProps and mapDispatchToProps with mapHooksToProps and wouldn't have to worry about touching the underlying component.

  3. Testing is also easier because we can test the component in isolation without the hooks baked in.

  4. It makes prop overrides possible. In the case of our EnhancedCounter, we could override the counter prop by doing <EnhancedCounter counter={10} />. A real world example of hooks making things harder to override includes material-ui's new styling approach via hooks. Because classes are provided via hook and no longer via props, we would need custom logic to override classes via props with the new hook-based approach:

export default function Hook(props) {
  let classes = useStyles();
  classes = {...classes, ...props.classes};
  return <Button className={classes.root}>Hook</Button>;
}

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

@Dudeonyx
Copy link

I find it strange that people keep wanting the hooks API to be Factory/HOC based when hooks where partly created in the first place as a replacement for existing HOCs and render props patterns.

Anyway, your example of a withHooks HOC should work as is, Is there a need to make it an official API?
There is nothing stopping the people at material-ui from using it, maybe raise an issue with them to adopt this approach citing the benefits you've mentioned.

@threepointone
Copy link
Contributor

Yeah, this looks simple enough that doesn't need to be in core. Folks are welcome to make whatever composition models they'd like, whether as a generic HOC, or making bespoke components that compose hooks, and pass it as props to their components.

@ntucker
Copy link

ntucker commented Oct 21, 2019

Feel free to contribute to https://github.com/ntucker/hook-hoc if you want.

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

No branches or pull requests

4 participants