Reputation: 3875
I created this very simple app to hopefully explain this problem.
I tried using memoization and callback, but I believe it's re-rendering because the playerArr is always changing once I type into the text input.
my actual lists are only 15 elements in size, but the re-render is causing it to become REALLY SLOW when typing into the input.
Any suggestions? I have a deadline and i'm getting stressed out =( will going back to non-hooks help? or implementing redux? not sure the performance factor.
function App() {
const [player1, setPlayer1] = React.useState({
firstName: "First",
lastName: "Last ",
id: uuidv4()
});
const [player2, setPlayer2] = React.useState({
firstName: "First",
lastName: "Last",
id: uuidv4()
});
const [player3, setPlayer3] = React.useState({
firstName: "First",
lastName: "Last",
id: uuidv4()
});
return (
<div>
<State
player1={player1}
player2={player2}
player3={player3}
setPlayer1={setPlayer1}
setPlayer2={setPlayer2}
setPlayer3={setPlayer3}
/>
</div>
);
}
//----------------------------------------------------------
export const State = React.memo(({player1, player2, player3, setPlayer1, setPlayer2, setPlayer3}) => {
const playerArr = [player1, player2, player3];
const setPlayerArr = [setPlayer1, setPlayer2, setPlayer3];
return (
<div>
<Playlist
playerArr={playerArr}
setPlayerArr={setPlayerArr}
/>
</div>
);
});
//----------------------------------------------------------
export const Playlist = React.memo(({playerArr, setPlayerArr}) => {
return (
<div>
{
playerArr.map((player, index) => (
<Player
key={player.id}
player={player}
setPlayer={setPlayerArr[index]}
/>
))
}
</div>
);
});
//----------------------------------------------------------
export const Player = React.memo(({player, setPlayer}) => {
const handleOnChange = React.useCallback((event) => {
const playerCopy = {...player};
playerCopy[event.target.name] = event.target.value;
setPlayer(playerCopy);
}, [player, setPlayer]);
return (
<div>
<input type={"text"} name={"firstName"} value={player.firstName} onChange={handleOnChange}/>
<input type={"text"} name={"lastName"} value={player.lastName} onChange={handleOnChange}/>
</div>
);
});
EDIT: i edited app per discussion. same thing happening
Upvotes: 1
Views: 101
Reputation: 11571
No matter what you do, your <App>
and <Playlist>
components (even if they are memoized) will HAVE to re-render every time there is a user input because that is where you are storing your state, and is to be expected.
The best you can do is memoize each <Player>
component so that when the list re-renders, every individual list item doesn't necessarily re-render itself. To do this you can pass an "areEqual" function as the second argument to React.memo
. See the example in the React documentation: https://reactjs.org/docs/react-api.html#reactmemo
In your case, it would probably look something like this:
export const Player = React.memo(({player, setPlayer}) => {
const handleOnChange = React.useCallback((event) => {
const playerCopy = {...player};
playerCopy[event.target.name] = event.target.value;
setPlayer(playerCopy);
}, [player, setPlayer]);
return (
<div>
<input type={"text"} name={"firstName"} value={player.firstName} onChange={handleOnChange}/>
<input type={"text"} name={"lastName"} value={player.lastName} onChange={handleOnChange}/>
</div>
);
}, (prevProps, nextProps) => {
// Check to see if the data is the same
if (prevProps.firstName === nextProps.firstName
&& prevProps.lastName === nextProps.lastName
&& prevProps.id === nextProps.id) {
return true; // Return true if they ARE the same
} else {
return false; // Return false if they are NOT the same
}
});
Sometimes, if the data you are comparing is a simple collection of strings and/or numbers, you can use JSON.stringify
as a shorthand way to convert it to a string and compare the strings:
export const Player = React.memo(({player, setPlayer}) => {
const handleOnChange = React.useCallback((event) => {
const playerCopy = {...player};
playerCopy[event.target.name] = event.target.value;
setPlayer(playerCopy);
}, [player, setPlayer]);
return (
<div>
<input type={"text"} name={"firstName"} value={player.firstName} onChange={handleOnChange}/>
<input type={"text"} name={"lastName"} value={player.lastName} onChange={handleOnChange}/>
</div>
);
}, (prevProps, nextProps) => {
// Check to see if the data is the same
if (JSON.stringify(prevProps) === JSON.stringify(nextProps)) {
return true; // Return true if they ARE the same
} else {
return false; // Return false if they are NOT the same
}
});
Upvotes: 1