I'm using the why-did-you-render package to debug some performance issues in my react app. I'm getting the following console message regarding why my XDrag component is re-rendering:
XDrag
defaultNotifier.js:40 {XDrag: ƒ} "Re-rendered because of props changes:"
defaultNotifier.js:45 props.children
defaultNotifier.js:49 different React elements (remember that the <jsx/> syntax always produces a *NEW* immutable React element so a component that receives <jsx/> as props always re-renders). (more info here)
defaultNotifier.js:52 {prev children: {…}} "!==" {next children: {…}}
In the article linked to in this message, they suggest using a "pure wrapper" component around our original component to avoid this rerender:
class PureFatherWrapper extends React.PureComponent{ render(){ return ( <PureFather> <SomeChild/> </PureFather> ) } }
import React, { FC, ReactNode } from "react";
import { Draggable, DraggableProps } from "react-beautiful-dnd";
interface IXDrag extends Omit<DraggableProps, "children"> {
className?: string;
children: ReactNode;
dragAll?: boolean;
}
const XDrag: FC<IXDrag> = ({ className, children, dragAll, ...props }) => {
console.log(React.isValidElement(children));
if (!React.isValidElement(children)) return <div />;
return (
<Draggable {...props}>
{(provided, snapshot) => {
const dragHandleProps = dragAll ? provided.dragHandleProps : {};
return (
<tr
className={className}
ref={provided.innerRef}
{...provided.draggableProps}
{...dragHandleProps}
>
{React.cloneElement(children, { provided })}
</tr>
);
}}
</Draggable>
);
};
Using XDrag:
import React from "react";
import { useStoreState } from "../../hooks";
import XDrag from "../XDrag";
import TableHeader from "./TableHeader";
const ContentTable = () => {
const cardItems = useStoreState(
(state) => state.appModel.activeCards
);
return (
<table>
<tbody>
<tr>
<TableHeader
title={"URL"}
/>
</tr>
{cardItems.map((card, i) => {
return (
<XDrag
draggableId={card.sourceId}
index={i}
key={i.toString()}
isDragDisabled={card.isActive}
>
<td>{card.src}</td>
</XDrag>
);
})}
</tbody>
</table>
);
};
Some Context:
I'm using the react beautiful dnd library to create some draggable rows in a table. Each XDrag provides a <tr> for all the <td>s.
I will try to answer each question and hopefully give you some extra information to help you make a decision.
In simple words, a pure component (or pure function) is a component (or function) that will always return the same output for the same input (by the way, this is not exclusive to React).
Example: function add(x, y){return x + y} is a pure function because add(1, 2) will always return 3, no matter how many times you run it.
PureComponents have the same idea. You can read more about PureComponents in the official docs.
In the article that you linked, this is the key part to answer this question:
Always remember: the
<jsx prop='a'/>syntax is just a sugar for:React.createElement('jsx', {prop: 'a'}). This means whenever it’s father re-renders, jsx tries to update itself with a new props object.
A PureComponent would know that the prop: a didn't change, so it would not rerender. This happens because of the Shallow Compare utility.
You could either use the suggestion from the article (create a PureWrapper component and use it to wrap your <XDrag>) or use the useCallback hook (or useMemo). Something like this:
const ContentTable = () => {
const cardItems = useStoreState((state) => state.appModel.activeCards);
const renderCardItems = useCallback(() => {
return cardItems.map((card, i) => {
return (
<XDrag draggableId={card.sourceId} index={i} key={i.toString()} isDragDisabled={card.isActive} >
<td>{card.src}</td>
</XDrag>
);
})
}, [cardItems])
return (
<table>
<tbody>
<tr>
<TableHeader title={"URL"} />
</tr>
{renderCardItems()}
</tbody>
</table>
);
};
Here, useCallback has a dependency array (like useEffect), which means that this function only changes if cardItems changes. In other words, renderCardItems() will always return the same result as long as cardItems is the same.
Hopefully this helped to clarify some of your questions.
That said, the real question is: "why do you need to optimize this rerender?"
We often worry about optimization when we don't need to. React is already pretty good with that, and browsers today have much more power. Unless you have a specific requirement or a really bad performance, you should not add complexity to your code just to avoid some rerenders.
In fact, "avoid early optmizations" is one clean code principle.
Here is a really good article with more information on that: https://kentcdodds.com/blog/usememo-and-usecallback
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With