Redux connect optimize mergeProps

2019-08-03 06:31发布

问题:

I have a list of items that is rendered by the list like so:

items.map(item => (
  <ListItem
    item={item}
    updates={updates[item.id]}
    {/** other props like deleting loading*/}
  />
));

Note that item here is not what is passed to the action creator functions, ListItem is a container that will change this.

My item container has an optimized mapStateToProps (it returns a function, not an object):

const mapDispatchToProps = {
  updateItem,
  //others
}
const mapStateToProps = () => {
  //create momoized state creator
  const memoizedStateCreator = createMemoized();
  return (unused, { item, updates }) =>
    //use memoized state creator
    memoizedStateCreator(item, updates);
};

In the item when I click to save the item I have something like this:

<button onclick={updateItem(item)}

I would like to change this to

<button onclick={updateItem}

For that I can use mergeProps but that would cause render of the Item (the Item function) to be called even though nothing changed because I always return a new reference as props. It would also cause react to do a shadow DOM compare of the result (instead of pure component that would skip that)

MergeProps cannot be optimized with a factory function (like mapStateToProps) where I can create a memoized function.

const mergeProps = (stateProps, dispatchProps) => ({
  ...stateProps,
  ...dispatchProps,
  updateItem: () => 
    stateProps.updateItem(stateProps.item),
});

One way to do this is to not use mergeProps at all and use a wrapper function instead:

const wrapper = props =>
  ListItem({
    ...props,
    updateItem: () => props.updateItem(props.item),
  });
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(memo(wrapper));

My question is; is there a way to do this with mergeProps without having to turn my functional components into classes?

Returning a function from mapDispatchToProps would not work in my case because item does not come from ownProps, it comes from mapStateToProps

回答1:

Assuming that requestDelete is an action that can be dispatched you should map it in mapDispatchToProps of your connected ListItem component. mapDispatchToProps can also be a function that receives the ownProps as the second argument which has the id:

const mapDispatchToProps = (dispatch, {item}}) => ({
    requestDelete: () => dispatch(requestDelete(item.id)),
});

export default connect(mapStateToProps, mapDispatchToProps)(ListItem);