Babel plugin and get a performance boost for your React components
Tags: babel, react
Published: 2021-07-29 09:54:17

> This article was published on Monday, January 21, 2019 by [Eytan Manor](https://twitter.com/eytan_manor) @ [The Guild Blog](https://the-guild.dev/)

With the introduction of React hooks (in React 16.8-alpha) arose an issue — calculations are being
unnecessarily re-evaluated due to declarations being done within the rendering phase.

To put things simple, if now we're using class components, and we store calculation results on the
class instance to save ourselves some precious processing power:

```jsx
class MyComponent extends React.Component {
  constructor(props) {
    super(props)

    this.transformedData = props.data.filter(props.filterPredicate).sort(props.sortComparator)
  }

  render() {
    return (
      <div>
        <button onClick={this.goBack} />
        <ul>
          {this.transformedData.map(({ id, value }) => (
            <li key={id}>{value}</li>
          ))}
        </ul>
      </div>
    )
  }

  goBack = () => {
    this.props.history.pop()
  }
}
```

In the near future, we will have no choice but to do everything within the rendering method itself,
dictated by hooks:

```jsx
const MyComponent = ({ data, history, filterPredicate, sortComparator }) => {
  const transformedData = data.filter(filterPredicate).sort(sortComparator)

  const goBack = () => {
    history.pop()
  }

  return (
    <div>
      <button onClick={goBack} />
      <ul>
        {transformedData.map(({ id, value }) => (
          <li key={id}>{value}</li>
        ))}
      </ul>
    </div>
  )
}
```

To solve this problem, the React team invented a couple of methods: `useCallback()` and `useMemo()`.
Each of them is used for different reasons but they're quiet similar, and essentially they're used
as guard functions that will re-activate themselves only if certain parameters were changed. I
recommend you to go through the
[official React docs](https://reactjs.org/docs/hooks-reference.html#usecallback) to get a better
perspective on these. If we were to implement it in the example above, it should look like so:

```jsx
const MyComponent = ({ data, history, filterPredicate, sortComparator }) => {
  const transformedData = useMemo(
    () => data.filter(filterPredicate).sort(sortComparator),
    [history, filterPredicate, sortComparator]
  )

  const goBack = useCallback(() => {
    history.pop()
  }, [history])

  return (
    <div>
      <button onClick={goBack} />
      <ul>
        {transformedData.map(({ id, value }) => (
          <li key={id}>{value}</li>
        ))}
      </ul>
    </div>
  )
}
```

Wait a minute… So does it mean that I have to wrap all my declarations in these hooks just to get
performance which is on a par with class components?!

![](https://the-guild.dev/medium/893729d5d857bd8f7ebcdfc388a150e8.jpeg)

That's right Vladimir. Even the React team suggested that, and I quote from their docs:

> “In the future, a sufficiently advanced compiler could create this array automatically” — React

It's a good thing I love React and I think of the future. That's why I invented this Babel plug-in
called `babel-plugin-react-persist`, and it addresses exactly that issue! All you have to do is edit
your `.babelrc` file and the code will be automatically transformed! Not only that, the plug-in also
takes care of optimizing inline anonymous functions in JSX attributes. This way each rendering phase
will have a similar instance of the intended callback. So given the following code:

```jsx
export default ({ data, sortComparator, filterPredicate, history }) => {
  const transformedData = data.filter(filterPredicate).sort(sortComparator)

  return (
    <div>
      <button className="back-btn" onClick={() => history.pop()} />
      <ul className="data-list">
        {transformedData.map(({ id, value }) => (
          <li className="data-item" key={id} onClick={() => history.push(`data/${id}`)}>
            {value}
          </li>
        ))}
      </ul>
    </div>
  )
}
```

The plug-in will generate:

```jsx
let _anonymousFnComponent, _anonymousFnComponent2

export default ({ data, sortComparator, filterPredicate, history }) => {
  const transformedData = React.useMemo(
    () => data.filter(filterPredicate).sort(sortComparator),
    [data, data.filter, filterPredicate, sortComparator]
  )

  return React.createElement(
    (_anonymousFnComponent2 =
      _anonymousFnComponent2 ||
      (() => {
        const _onClick2 = React.useCallback(() => history.pop(), [history, history.pop])

        return (
          <div>
            <button className="back-btn" onClick={_onClick2} />
            <ul className="data-list">
              {transformedData.map(({ id, value }) =>
                React.createElement(
                  (_anonymousFnComponent =
                    _anonymousFnComponent ||
                    (() => {
                      const _onClick = React.useCallback(
                        () => history.push(`data/${id}`),
                        [history, history.push, id]
                      )

                      return (
                        <li className="data-item" key={id} onClick={_onClick}>
                          {value}
                        </li>
                      )
                    })),
                  { key: id }
                )
              )}
            </ul>
          </div>
        )
      })),
    null
  )
}
```

So what are you waiting for? Go visit the official
[GitHub repo](https://github.com/DAB0mB/babel-plugin-react-persist) and get yourself a copy of the
plug-in! Have any suggestions or feature request? Feel free to open a ticket in the
[repo's issues page](https://github.com/DAB0mB/babel-plugin-react-persist/issues) or comment below!