2 May 2019

Benchmark the alpha-released hooks API in React Redux with alternatives

To compare with reactive-react-redux
Share on:

Introduction

Recently, React Redux released hooks API. It’s v7.1.0-alpha.4 as of writing.

https://github.com/reduxjs/react-redux/releases/tag/v7.1.0-alpha.4

On the other hand, I’ve been developing a new React Redux binding library with hooks and Proxy.

https://github.com/dai-shi/reactive-react-redux

It’s time to benchmark both of them to have better understanding in performance. The reactive-react-redux library utilizes Proxy to auto-detect state usage, hence it technically has overhead which would affect performance. We would like to learn how much it will be affected in a realistic example.

As a benchmark tool, js-framework-benchmark is used. For comparison, bare react-hooks implementation and connect-based react-redux implementation are also included.

Benchmark results

The following is the results of four implementations. Please refer the tool doc for detailed information.

screenshot1

screenshot2

screenshot3

The react-hooks result is basically the best (with some exceptions) because its implementation is basic without Redux. The react-redux-hooks result is pretty good and sometimes better than the react-redux result. The reactive-react-redux result is mostly OK and notably it’s relatively good in memory allocation.

The code at a glance

Let’s quickly look at the code. The following is from react-redux-hooks benchmark code.

const Row = React.memo(({ i }) => {
  const selector = useCallback((state) => {
    const item = state.data[i];
    return state.selected === item.id ? { id: item.id, label: item.label, selected: true } : item;
  }, [i]);
  const { selected, id, label } = useSelector(selector);
  const dispatch = useDispatch();
  const onSelect = useCallback(() => {
    dispatch(select(id));
  }, [dispatch, id]);
  const onRemove = useCallback(() => {
    dispatch(remove(id));
  }, [dispatch, id]);
  return (
    <tr className={selected ? "danger" : ""}>
      <td className="col-md-1">{id}</td>
      <td className="col-md-4"><a onClick={onSelect}>{label}</a></td>
      <td className="col-md-1"><a onClick={onRemove}>{GlyphIcon}</a></td>
      <td className="col-md-6"></td>
    </tr>
  );
});

const InnerRowList = React.memo(({ data }) => {
  return data.map((item, i) => <Row key={item.id} i={i} />);
});

const RowList = () => {
  const data = useSelector(state => state.data);
  return <InnerRowList data={data} />;
};

You may find that it uses React.memo several times, which is to optimize without connect.

The following benchmark code is for reactive-react-redux.

const Row = React.memo(({ item, selected }) => {
  const { id, label } = item;
  const dispatch = useReduxDispatch();
  const onSelect = useCallback(() => {
    dispatch(select(id));
  }, [dispatch, id]);
  const onRemove = useCallback(() => {
    dispatch(remove(id));
  }, [dispatch, id]);
  return (
    <tr className={selected ? "danger" : ""}>
      <td className="col-md-1">{id}</td>
      <td className="col-md-4"><a onClick={onSelect}>{label}</a></td>
      <td className="col-md-1"><a onClick={onRemove}>{GlyphIcon}</a></td>
      <td className="col-md-6"></td>
    </tr>
  );
});

const InnerRowList = React.memo(({ data, selectedId }) => {
  return data.map((item, i) => <Row key={item.id} item={item} selected={selectedId === item.id} />);
});

const RowList = () => {
  const state = useReduxState();
  const data = state.data;
  const selectedId = state.selected;
  return <InnerRowList data={data} selectedId={selectedId} />;
};

Due to the nature that useReduxState in reactive-react-redux doesn’t take a selector, the code structure is slightly different. It passes an item object and a selected flag, instead of an i index, to the Row component. This code style is similar to that of react-hooks benchmark code.

For concrete benchmark code, refer the repo.

Conclusion so far

For this benchmark, the alpha hooks API in React Redux is already performant as is. The useSelector hook is straightforward for transition from connect.

The result for reactive-react-redux shows comparable performance. The overhead of Proxy should be tolerable for non intensive use cases. The coding style is different from react-redux, so it should be seen not as a replacement of react-redux, but as a new coding style with React hooks and Redux. Because it’s similar to bare react-hooks benchmark code, it could be more intuitive and fit well for people who newly learn Redux.

comments powered by Disqus