'Zustand is updating all the components on single property state change
Two different child components using the Zustand store are rendered on every state change. Though the state property they are using in the component is not updated. I am not using the entire store in the components, just try to utilize store slices. Here are the components
//Store
import create from "zustand";
export const useStore = create((set) => ({
shows: [
{
id: Math.floor(Math.random() * 100),
name: "River Where the Moon Rises",
},
{
id: Math.floor(Math.random() * 100),
name: "The Crowned Clown",
},
],
title: 'Default Title',
addShow: (payload) => set((state) => {state.shows.push({id:Math.floor(Math.random() * 100), name:payload})}),
updateTitle: (newTitle) => set({title:newTitle}),
}));
//App - component
function App() {
return (
<>
<ShowManagement />
<TitleManagement />
</>
);
}
export default App;
//ShowManagement - component
import React from 'react'
import { useStore } from "../hooks/useStore";
const ShowManagement = () => {
const { shows } = useStore((state) => ({ shows: state.shows }));
const { addShow } = useStore((state) => ({addShow: state.addShow }));
console.log('ShowManagement - reloaded');
return (
<>
<div>ShowManagement</div>
<ul>
{shows?.map((drama) => {
return (
<li>
{drama.id} - {drama.name}
</li>
);
})}
</ul>
<div>
<input width={200} id="dramaText" />
<button
onClick={() => addShow(document.getElementById("dramaText").value)}
>
Add Drama
</button>
</div>
</>
);
}
export default ShowManagement
//TitleManagement - component
import React from 'react'
import { useStore } from "../hooks/useStore";
const TitleManagement = () => {
const { title } = useStore((state) => ({title:state.title}));
const { updateTitle } = useStore((state) => ({updateTitle: state.updateTitle}));
console.log("TitleManagement - reloaded");
return (
<div>
<p>{title}</p>
<button
onClick={() => updateTitle('Title From UI')}
>
Update Title
</button>
</div>
);
}
export default TitleManagement
The component should not be rendered on other state property changes.
Solution 1:[1]
Try this.
Not sure why you are creating a "new" object on your return, when you're just destructuring the value out anyways. So, for all your state selectors, don't return a new object - that will "always" be a new reference, so you'll always get a new re-render.
Just get the state you need and don't get fancy on what you return.
const title = useStore((state) => state.title);
const updateTitle = useStore((state) => state.updateTitle);
This seems to work: https://codesandbox.io/s/elegant-rain-fy4g4v?file=/src/App.js
please keep in mind that if you're using react 18, you're gonna see two renders. So, in that codesandbox, I am using react 17'ish... along with zustand 3+
If you don't care about the two renders (for the state changes), then up them to 18 and 4, respectively.
Solution 2:[2]
So, I also just got started using this library about an hour ago and found it pretty awesome, but was stuck on the same question.
The problem is in the comparison function. You can inspect this by adding your own comparison with an extra log:
const { shows } = useStore((state) => ({ shows: state.shows }), (a, b) => {
console.log('comparing', a, b)
return a === b
})
and you'll see that two objects are being compared (which won't return true on the comparison and thus always update).
Turns out the answer is in the readme:
import shallow from 'zustand/shallow'
const { shows } = useStore((state) => ({ shows: state.shows }), shallow)
or simply write your own comparison function
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | phaze |