Back to Blog
August 05, 2020·8 min read

A Retrospective on React Hooks

Hooks fundamentally changed React.They enable us to compose behavior in ways class components never could.But they also introduced the "Rules of Hooks" and the dependency array confusion.

React Hooks Code

The Sync Mental Model

Before Hooks, we thought in "Lifecycle Methods": componentDidMount , componentDidUpdate, componentWillUnmount . We mapped our code to time.

With Hooks, especially useEffect , we must think in Synchronization.The question isn't "When does this run?" The question is "What state is this effect matching?"

 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        // Wrong: Thinking in time
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        useEffect(() => {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            fetchData();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        }, []); // "Run once on mount"

// Right: Thinking in synchronization
useEffect(() => {
    fetchData(query);
}, [query]); // "Keep data synchronized with query"

Stale Closures: The New "this"

In classes, this.state.count always pointed to the latest count. In Hooks, we deal with closures. If your Effect captures a variable from the first render, it will gaze into the past forever.

The solution is not to lie to the dependency array.If you use it, list it.If listsing it causes infinite loops, your Effect is likely doing too much or your logic structure is wrong(e.g., defining functions inside components without useCallback ).

Custom Hooks: The Composition Superpower

This is where Hooks shine.In the Class era, sharing non - visual logic required "Higher Order Components"(HOCs) or "Render Props." Both led to "Wrapper Hell."

Custom hooks allow us to extract stateful logic just like we extract utility functions.

 
                function useWindowSize() {
                    const [size, setSize] = useState(window.innerWidth);
                    useEffect(() => {
                        const handleResize = () => setSize(window.innerWidth);
                        window.addEventListener('resize', handleResize);
                        return () => window.removeEventListener('resize', handleResize);
                    }, []);
                    return size;
                }
                

This useWindowSize hook can be used in any component.The state is isolated, but the logic is shared.This has led to the explosion of hook libraries(useHooks, react - use).

The Rules of Hooks

Hooks rely on call order.React knows that the first useState call corresponds to the first state variable because it relies on the index in an internal array. This is why you cannot put Hooks for loops or if statements. It feels magical and fragile initially, but it's the constraint that enables the conciseness.

useContext + useReducer != Redux

A common 2020 trope was "You don't need Redux anymore." While useContext + useReducer can manage global state, they lack the performance optimizations of Redux(selector subscriptions).Updating a Context value re - renders all consumers. For complex state, purpose-built libraries like Redux Toolkit, Zustand, or Jotai are still superior.

Conclusion

Hooks have won.They are the default.Class components are legacy.The learning curve is stepper(you need to understand closures deeply), but the expressiveness and composability are worth it.We are writing less code and building better abstractions.


References

A Retrospective on React Hooks | Akash Deep