Is it okay to connect a PureComponent?
Actually connect()
function makes the wrapped component pure by default (see docs). That is, the wrapped component will be rerendered only when the properties change (state or own props). So there's no point in inheriting from PureComponent, because shouldComponentUpdate
logic is already implemented in HOC produced by connect()
.
I've heard that PureComponents can slow down performance...
Shallow props comparison performed by PureComponent is relatively cheap operation. I don't think it's going to be an issue.
There is no issue in using connect and PureComponent. PureComponent renders if the props have changed and connect()
maps the redux state to props. See in this example by the redux team. I have replaced the TodoList component with a Purecomponent:
class TodoList extends React.PureComponent {
render() {
const { todos, toggleTodo } = this.props;
return (
<ul>
{todos.map(todo => (
<Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} />
))}
</ul>
);
}
}
/*
const TodoList = ({ todos, toggleTodo }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => toggleTodo(todo.id)}
/>
)}
</ul>
)
*/
It works just the same.
I had a problem with list items that were connected components and ended up here after googling for it.
I will add the description of the problem and my solution here:
The mapStateToProps
looks something like this
import { defaultMemoize } from 'reselect';
const mapStateToProps = () => {
const createMergedItem = defaultMemoize((item, edit) =>
edit
? { ...item, edit: true }
: { ...item, edit: false }
);
return (state, { item, edits }) => {
//returning same ref when item and edits[item.id] didn't change
return createMergedItem(item, Boolean(edits[item.id]));
};
};
export default connect(
mapStateToProps,
)(Item);
In the List component
items.map(item=>(<Item key={item.id} item={item} edit={edit} />)
The code is a bit simplified but what it does is that List passes an item and edit to each Item component as props, edit is an object that has members with an item.id as key. If I have an item with id 1 and edits is {1:anythingTruthy}
then the item 1 is in edit mode.
When I'd change an item in the list from or to edit mode then all the items in the list that weren't in changed would re render even though mapStateToProps would return the same reference as last time.
I always thought that connect would return a pure component but I was mistaken, the solution is to make Item a pure component and with React.memo this is very simple:
import { memo } from 'react';
//mapStateToProps is the same
export default connect(
mapStateToProps,
)(memo(Item));//wrap Item in memo
Where Item is a functional component (props=>jsx
).
when you change edit mode of one item in the list the edit
prop will change for all items but thanks to defaultMemoize
and returning a function from mapStateToProps
that crates a memoized createMergedItem
function it'll return props that has the same reference as the last one. This was not enough because the Item function was still called. I had to also use React.memo
to make Item a pure component.