The class version is class ViewportHook {
constructor(component) {
this.viewportState = component.addState(this, initialValue)
component.addEffect(() => {
return watchViewport((x, y) => this.viewportState.set({something: x + y})
})
}
value() { return this.viewportState.get() }
}
The hook version is function useViewport() {
const [state, setState] = useState(initialValue);
useEffect(() => {
return watchViewport((x, y) => setState(x + y))
}, [])
return state;
}
I changed the API to be more similar to the existing useEffect where you can return the unsubscribe function. That makes the difference in the size of code non-significant.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. |
Sure! You show ViewportHook's constructor receiving a "component" prop. Since, in the calling component, you call this.use(ViewportHook), the `use` method is supposed to pass some sort of reference of the calling component into ViewportHook's constructor. My question was about what the type of that parameter is. Internally, is `use` something like `use(Hook) { return new Hook(this); }`? If so, you're passing a direct reference to the class instance. I had thought that maybe the framework could pass a more limited delegate for the instance to the constructor to prevent people from doing something silly with it, but that would prevent you from assigning a property safely anyway.
> 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.
Object properties can't be safely minified. Any user-defined component using lifecycles will still need to have "constructor", "render", etc in the generated source, whereas identifiers (like "MyComponent" in `const MyComponent = ...`) aren't accessed by being looked up on an object, and thus can safely be minified to one or two character names. This was one of the motivations of the design of hooks. I believe it's called out in the original talk from React Conf 2018.
[Edit]
And regarding point 4: componentDidUpdate receives prevProps and prevState, and shouldComponentUpdate receives nextProps and nextState. How would you provide information about the previous or next version of that hook-based state if it's being tracked as an instance property rather than in the component's state object?