This is a hook version of the same code as near as I could guess it. function useViewport(initialValue) {
const [state, setState] = useState(initialValue);
useEffect(() => {
return watchViewport((x, y) => setState(x + y))
}, [])
return state.get();
}
// usage
function MyComponent() {
const viewportSize = useViewport()
// use in render
}
I made a couple of assumptions here. From usage, I assume watchViewport is supposed to both subscribe and return a teardown function. I also assume that the viewportState.set/get are functions for getting and setting the tracked value in the component's state.In my opinion, there are many advantages to the hook version and several major disadvantages to the class version: 1. There's no need for hooks to have a value method or React.Components to grow the use or addState methods because the hook is just a function call which returns a value. How it produces that value is up to the code inside the function--which does call out to React--but the fact the function will always be called when MyComponent is called (absent something throwing earlier in the function body of course). The value you see being returned by the hook will always match the value you get when you call useViewport() in your component. These two things are guaranteed to have the exact same behavior as any other JavaScript function call and thus you can reason about the "registration" and value passing without needing to learn any framework-specific APIs. 2. There's no need for an addOnUnmount method on the component object passed to the ViewportHook constructor, because the hook function can use the function component's equivalent to componentWillUnmount (the return value of a useEffect) in the exact same way as a function component can, but without need for external registration. 3. In order to implement the class API, you'd need to either (A) pass the component's actual instance to the hook, or (B) create a new type of value to represent the instance of the component the hook is registering against. Option (B) is yet another API to learn, as you have to learn a new type of object to deal with in a React application. Option (A) would mean figuring out a way to prevent people from calling those methods after construction, OR introducing the possibility of registering a new slice of state partway through. The latter might be possible, and maybe that's even what you intend, but I'd want to know what the expected impact on methods like shouldComponentUpdate or getDerivedStateFromProps would be. Speaking of those... 4. I can't think of any obvious way you could pass previous versions of hook-related instance properties to lifecycle methods in the same way that you can pass prevProps and prevState. 5. Concision: the hook version has a dramatic reduction in the amount of code you have to read 6. Bundle size: because the hook version relies on functions rather than class properties, it can be minified trivially and thus reduce bundle size even more than the obvious reduction in character count would imply |
Again, its more about API design rather than classes or functions.
Another crucial benefit of the class version: you can use hooks in conditions or loops, in any order. This is because they're not called on every render. I also used a unique `key` to pass to `addState`, but its not really a requirement, since the hook constructors only get called once.
I don't understand points 3 and 4, can you elaborate? I'm assuming that when i call `component.use(HookClass)` the component creates a new instance of that hookclass. Regarding learning, I don't think the whole concept of (functional) hooks and their idiosyncracies are any easier to learn than learning one new type of class.
> Bundle size: because the hook version relies on functions rather than class properties, it can be minified trivially and thus reduce bundle size even more than the obvious reduction in character count would imply
I don't think this will make any meaningful difference. The full component methods API (addState, addEffect) is likely to be in use in any nontrivial project, so that can't be minified away. For 3rd party hook classes, they would be small and independend through `use` and easy to minify / dead-code-eliminate if not in use.