Why We Switched to React Hooks

Erica Edge
Bits and Pieces
Published in
7 min readSep 11, 2019

--

Have you ever been tasked with extending a component that has a ton of branching logic, and realize that the component requires some state to make that new feature you’re adding come to life? Have you ever felt the pain of transforming a component, and all of its UI and business logic, from a stateless functional component to a class, just to toggle some css to change the text from its default color to red? Have you ever had to really think about how data should be shared across deeply nested components?

Chances are that, if you are a React developer, this may sound like your life; and hopefully by now, you’ve heard about Hooks and have at least played around with them in your current projects.

What are Hooks?

“Hooks are a new addition to React in version 16.8 that allows you use state and other React features, like lifecycle methods, without writing a class.”

Dan Abramov has provided a well-rounded introduction to the API, and how and why the framework that many of us use has evolved to where it is now. You can read about it and watch his ReactConf 2018 keynote introducing the hooks proposal here.

The key takeaway may be this:

If the React community embraces [hooks], it will reduce the number of concepts you need to juggle when writing React applications. Hooks let you always use functions instead of having to constantly switch between functions, classes, higher-order components, and render props.

Classes in JavaScript encourage multiple levels of inheritance that quickly increase overall complexity and potential for errors; anything that allows for composition with plain functions is a win. At OddBird, we were excited to leave class components behind in our latest greenfield project, and have greatly benefited from having simple, consistent patterns to manage state, and share stateful logic across components through embracing the new hooks API.

Managing Application State

Code that is easy to reason about is basically code that you can do in your head. With hooks, you can (and probably should) still use local state to manage data for UI reactivity. Choosing to use stateless functional components removes the trade-offs that come with idiomatic JavaScript classes, such as boilerplate constructor initialization and binding functions to components for its methods to have access to this in the component instance

With hooks, this component that maintains its own internal state data

(See Sandbox: Timer Component with Class by duggiemitchell.)

…can be simplified to half the lines of code, achieving the same results.

(See Sandbox: Timer Component with Hook by duggiemitchell.)

In the first example, we can see there are three separate methods (componentDidUpdate, componentDidMount, and tick) to accomplish the same thing: keep the count displayed in the UI in sync based on the value seconds.

One of the greatest things about React is its declarative nature. While this is mostly true, the imperative setState (and possibly lifecycle methods) are actually a deviation from this core characteristic. With hooks, code can be written more declaratively with almost no branches, making it easier to follow.

Sharing State with Context

This may or may not be a popular opinion, but I believe that React is its own state management library. However, most projects I’ve worked on have turned to a predictable state container such as Redux for an elegant solution to the prop drilling problem that comes from lifting up state.

The introduction of the Context API provided a simpler interface for sharing state that each component may need access to — such as an authenticated user, or a theme object — without the need to pass props through intermediate components. It could arguably replace the need for Redux in your front-end architecture, but is used for any data considered global for any tree of React components.

Create a Context object:

const CountContext = React.createContext({})

useContext

The useContext hook is provided for setting values from context providers, and offers the same benefits of not having to manually pass data to components and the ability to pass state exclusively to components that need to know it.

Use a Context object in a nested component.

(See Sandbox: Timer with useContext by duggiemitchell.)

This may not seem like much to look at, but this is a big improvement from the old way of using the render prop pattern to read context values, which can become hard to read should your component need to read from multiple contexts.

Using Hooks with Redux

We use Redux to manage state because of the action/reducer patterns helps to keep state deterministic and organized. React Redux provides official React bindings for Redux and now offers hooks as an alternative to using the connect Higher Order Component (HOC) to subscribe to and make changes to data in your Redux store.

This has its advantages, but react-redux hooks get a bit more tricky with memoizing selectors and avoiding unnecessary re-renders; we may or may not be using useMemo and useCallback in the right way in all the right places.

useReducer

Although listed in the React docs under Additional Hooks, it’s important to note that useReducer is an alternative to useState and the recommended way to manage component state when your application data is more complex, containing objects with multiple sub-values, or when the next state depends on the previous one.

It accepts a reducer of type (state, action) => newState and returns the current state along with a dispatch method. Sound familiar? This pattern is powerful and could potentially have a number of use cases.

Here is out Timer component rewritten to update state using a reducer instead of local state.

(See Sandbox: Timer with useReducer by duggiemitchell.)

Sharing Non-Visual Logic

Before hooks, React didn’t have a basic way of extracting and sharing non-visual logic and from that limitation came other patterns like HOCs or Render props to solve a common problem. The ability to create custom hooks has been our biggest win because it allows us to extract stateful logic to a simple JavaScript function.

Here we take the stateful logic from our Timer component to create our own custom hook.

(See Sandbox: Timer Custom Hook by duggiemitchell.)

Custom hooks are a composition of the basic hooks like useState and useEffect encapsulated in a JavaScript function whose name starts with “use”.

A good rule of thumb could be, if you find yourself doing something often, like handling input data or toggling component visibility, why not create a custom hook for it?

(See Sandbox: useForm Custom Hook by duggiemitchell.)

In Closing…

Embracing Hooks as benefited us in the following ways:

✔️ How we managing state has become easier to reason about
✔️ Our code is significantly simplified, and more readable
✔️ It’s easier to extract and share stateful logic in our apps.

If our motivators aren’t enough for you to give it a try, the official documentation has some decent arguments to consider. At this time, we aren’t sure where Redux fits in with our use of hooks. We certainly could do without Redux using useReducer but the jury is still out on that.

Remember, it isn’t necessary to refactor all of your stateful components to use hooks, (unless that appeals to the business and your get their buy-in) the existing APIs aren’t going anywhere. But if your team decides to give them a try, I hope our experience is helpful in that decision making.

Hi, I’m Erica Mitchell (aka Duggie) and I’m a Senior Frontend Developer at Oddbird, a small agency of developers and designers who help clients create scalable, and performant web applications with a human-centered design.

If you like my style, go ahead and give a round of applause, and be sure to follow to read more of my rambling thoughts about life && code ❤️.

--

--