https://codesandbox.io/s/5vvm68k6qp. The custom effects would take functions instead of values and then `useEffect` will be executed by the component. There's certainly some issues in this implementation with regard to how to handle nested state changes, but I think that can be worked out.
I like judofyr's solution to this as well, but I think hooks that change the internal state of component are a bad idea, and breaks encapsulation in my mind.
I've used the hooks myself and I like the composability, but I can also understand the vitriol from the community. And so far my biggest complaint is that I can't use them in classes as well. I think if you treat them as a more core/composable type of object then you can use them in the same paradigm as Component classes.
My example here changes the names of the typical lifecycle events for clarity but you could use the component lifecycle names instead. Point is it should match the component lifecycle and allow the user to hook into each one separately but wrap everything up into a meaningful collection.
A hook must be declaratively defined as a property in another class (I use the @annotation syntax here but I realize it's not standard yet), and that allows react to keep a consistent ordering on how/when hooks are called during lifecycle events. It also allows the hook to register itself for the lifecycle of the owner.
<code>
class UseWindowWidth extends Hook {
// normal style of using state
state = {
width: this.props.value
}
// or maybe use state hook for new style
//@hook(UseState, this.props.value)
//width;
handleResize = (evt) => {
this.width.set(window.innerWidth);
}
hookDidMount() {
window.addEventListener('resize', this.handleResize);
}
hookWillUnmount() {
window.removeEventListener('resize', this.handleResize)
}
getWidth() {
return this.state.width;
}
// other public methods here would allow owner to do things
// ? although this doesn't fit well with top-down props style
}
class MyComponent extends Component {
// @hook registers the hook into lifecycle events and
// passes props to UseWindowWidth hook. Also, any changes
// to hook state should cause a forceUpdate of the component
@hook(UseWindowWidth, { value: window.innerWidth })
windowWidth;
render() {
let width = this.windowWidth.getWidth();
return (
<p>Window width is {width}</p>
);
}
}
</code>
The 'props' concept might not make sense with the Hook object as I have it here, cause I can't see a nice way to declaratively update the props instead of just calling setters, but that might be ok for how these would be used.
I'm not in love with this yet, and there's some other things that would have to be ironed out (like coordination between hooks), but I think it opens some ways to allow hook use in class components as well.
Note Hooks execute on every render. That’s their whole point.
I explain this here: https://overreacted.io/why-do-hooks-rely-on-call-order/#flaw...