Veera / Blog

Rewriting a React Component using Hooks and React.Memo

Remember Gauge Generator app? I initially wrote it using React classes and then optimized it's performance using React PureComponent.

Now let's see how it can be rewritten using the talk-of-the-react-town - Hooks.

What the hook?

Use React Hook

Put it simply, Hooks lets you to use state and other react features in your function components. For example, if you need state, you can use useState hook. And, if you need context, you can use useContext.

Apart from the built-in hooks, you can create your own custom hooks and share the functionality across your React components (remember mixins, but a LOT better than that).

You can get an overview of React hooks or if you are really interested, do a deep dive in Dan's blog.

Let's get on to our business - convert a React class component that uses State to a function component using Hooks.

React class component with State

The component that I am going to use is the InputColor of Gauge Generator app. This component wraps the SketchPicker color picker tool and let the user to select a color from the pop-up. It has one state variable - pickerOpen to indicate if the pop-up is opened or not.

For brevity, I omitted some code of this class.

import React, { PureComponent } from "react"

class InputColor extends PureComponent {
  state = {
    pickerOpen: false
  }

  togglePicker = () => {
    this.setState(state => ({ pickerOpen: !state.pickerOpen }));
  }

  render() {
    const { pickerOpen } = this.state;
    return (
      <div>
        Picker State: {pickerOpen ? "Open" : "Closed"}
        <button onClick={this.togglePicker}>Toggle</button>
      </div>
    );
  }
}

Here we have a simple React component, written as a Class and holds a State variable. To convert this into a functional component with hooks, lets determine the state we need to track. In our case, we need to track a single pickerOpen boolean value in state. We can use useState hook for this purpose.

useState hook

useState function takes in an initial value of the state and returns two values: [currentStateValue, callbackToSetStateValue]

So, in our case, we can convert the state and its modified method as below:

Refactoring state

/* Before refactor */
state = {
  pickerOpen: false
}

togglePicker = () => {
  this.setState(state => ({ pickerOpen: !state.pickerOpen }));
}

/* After refactor using useState hook */
const [pickerOpen, setPickerOpen] = useState(false);
const handlePickerButtonClick = e => {
  setPickerOpen(!pickerOpen);
}

If you notice, we got rid of using this in the refactor. Because, in functional component, you don't need to use this and all the functions / variables are wrapped in closures, so you can directly access them.

With this change, now whenever you update the state value using setPickerOpen callback, React will re-render your whole component again.

Now that we have our state built, it's time to use it in our render logic.

Refactoring render

const [pickerOpen, setPickerOpen] = useState(false);
const handlePickerButtonClick = e => {
  setPickerOpen(!pickerOpen);
}
return (
  <div>
    Picker State: {pickerOpen ? "Open" : "Closed"}
    <button onClick={handlePickerButtonClick}>Toggle</button>
  </div>
);

Putting it all together

We have our state and render function, lets wrap it in a functional React component:

const InputColor = () => {
  const [pickerOpen, setPickerOpen] = useState(false);
  const handlePickerButtonClick = e => {
    setPickerOpen(!pickerOpen);
  }
  return (
    <div>
      Picker State: {pickerOpen ? "Open" : "Closed"}
      <button onClick={handlePickerButtonClick}>Toggle</button>
    </div>
  );
}

No more classes! yay!!

Hey, what about the performance?

React will re-render your function component when ever there's a change in the prop values. In Class component, we have PureComponent or shouldComponentUpdate to help us with avoiding re-renders. PureComponent will do a shallow comparision and using shouldComponentUpdate, you can write your own comparison logic.

But, How can we do the same in the function component? React.memo.

Just wrap your function component with React.memo and you will get the functionality of PureComponent in here.

For example,

const InputColor = React.memo( ({value}) => {
  /* ... other logic ...*/
    return <p>{value}</p>
});

With this change, React will not re-render our InputColor component, if the passed in value did not change.

Conclusion

In this post, I touched upon the very basic of React hooks - useState. In the coming articles, lets dive further and refactor our Gauge Generator app using some advanced hooks concept. Hang tight!