简体   繁体   中英

How to use React.memo with react-redux connect?

Does anyone know how to wrap a React component with React.memo when one is using the connect function from react-redux?

For example, how would you modify the following?

let Button = (props: Props) => (
  <button onClick={props.click}>{props.value}</button>
);

Button = connect(
  mapStateToProps,
  mapDispatchToProps
)(Button);

I've tried:

let Button = React.memo((props: Props) => (
  <button onClick={props.click}>{props.value}</button>
));

Button = connect(
  mapStateToProps,
  mapDispatchToProps
)(Button);

However, the function returned by connect expects a component to be passed so it errors with:

Uncaught Error: You must pass a component to the function returned by connect. Instead received {"compare":null}

React.memo is nothing but a HOC, so you can just use:

Without memo:

connect(
  mapStateToProps,
  mapDispatchToProps
)(Button);

With memo:

connect(
  mapStateToProps,
  mapDispatchToProps
)(React.memo(Button));

And even wrap to connect: (This should be the solution with connect)

React.memo(
  connect(
    mapStateToProps,
    mapDispatchToProps
  )(Button)
);

Like we do with withRouter : withRouter(connect(...)())

Same issue here. Fixed by upgrading react-redux to version 5.1.0.

Your solution should work (I didn't try copy-pasted like that), but you also have to update react-redux to the latest version.

By the way, I think the proper implementation of React.memo within many HOC would be to be the closest to the component itself : the goal of React.memo is to check if all the new props received by the component are the same as the last props. If any HOC transforms or adds any props to the component - which connect does by mapping the Redux store to the props, React.memo should be aware of it in order to decide wether or not to update the component.

So I would go for something like that :

//import what you need to import

const Component = props => <div>{/* do what you need to do here */}</div>

export default compose(
  connect(mapStateToProps, dispatchToProps),
  /* any other HOC*/
  React.memo
)(Component);

Codesandbox demo

As the error message says, you need to pass a component to the returned function from connect .
( which means the second pair of () in connect()() )

As React.Memo returns a component, pass it into the second function of connect .
Here's how you can do this.

export const MemoizedDemoComponent = connect(mapStateToProps)(React.memo(DemoComponent);

Demo component:

import React from "react";
import { connect } from "react-redux";

const DemoComponent = props => (
  <div>
    <p>My demo component fueled by: {props.fuel}!</p>
    <p>Redux: {props.state}</p>
  </div>
);

const mapStateToProps = state => ({
  state: "your redux state..."
});

// create a version that only renders on prop changes
export const MemoizedDemoComponent = connect(mapStateToProps)(
  React.memo(DemoComponent)
);

For a working example check also codesandbox .

For someone who want to know why react-redux throw this error.

For me, I used version 5.0.7 , react-redux/src/components/connectAdvanced.js line: 92

invariant(
  typeof WrappedComponent == 'function',
  `You must pass a component to the function returned by ` +
  `${methodName}. Instead received ${JSON.stringify(WrappedComponent)}`
);

After upgrading this code is changed to:

invariant(
  isValidElementType(WrappedComponent),
  `You must pass a component to the function returned by ` +
  `${methodName}. Instead received ${JSON.stringify(WrappedComponent)}`
);

How to check the WrappedComponent is changed to isValidElementType(WrappedComponent) which is exposed by react-is

So, yarn update react-redux to the version that mentioned by @Maxime Chéramy at least

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM