I have this zustand store
interface PropertyStore {
cache: Record<PropertyData["id"], PropertyData>;
fetchProperty: (propertyId: PropertyData["id"]) => StoreResponse<undefined>;
searchProperties: (filter: PropertySchemaFilter) => StoreResponse<undefined>;
createProperty: (newPropertyData: CreatePropertyData) => StoreResponse<undefined>;
updateProperty: (propertyId: PropertyData['id'], updatedProperty: UpdatePropertyData) => StoreResponse<undefined>;
}
export const usePropertyStore = create<PropertyStore>((set, get) => {
const cacheProperties = (properties: PropertyData[]) => {
const { cache } = get();
const cacheCopy = { ...cache };
properties.forEach((property) => {
cacheCopy[property.id] = property;
});
set({ cache: cacheCopy });
};
const newPropertyHandler = (newProperty: PropertyData) => {
console.log(`event -> [${WsEvent.NewProperty}] ${newProperty.address} `);
cacheProperties([newProperty]);
}
const updatedPropertyHandler = (updatedProperty: PropertyData) => {
console.log(`event -> [${WsEvent.UpdatedProperty}] ${updatedProperty.address} `);
cacheProperties([updatedProperty]);
}
propertiesSubscribe(newPropertyHandler, updatedPropertyHandler);
return {
cache: {},
fetchProperty: async (propertyId: PropertyData["id"]) => {
const { data: property, error } = await getProperty(propertyId);
if (error) {
return { error };
}
if (property) {
cacheProperties([property]);
}
return { data: undefined };
},
searchProperties: async (filter) => {
console.log(`propertyStore -> searchProperties ${JSON.stringify(filter)}`)
const { error, data } = await getProperties(filter);
if (error) {
return { error };
}
cacheProperties(data);
return { data: undefined };
},
createProperty: async (newPropertyData) => {
const { error } = await createProperty(newPropertyData);
return error ? { error } : { data: undefined };
},
updateProperty: async (propertyId, updatedProperty) => {
const { error } = await updateProperty(propertyId, updatedProperty);
return error ? { error } : { data: undefined };
},
};
})
A component that accesses the data by
const properties = usePropertyStore((store) => // <- line 31
Object.values(store.cache).filter((property) =>
propertyCompliesFilter(property, propertiesFilter) // <- line 33
)
);
const searchProperties = usePropertyStore((store) => store.searchProperties);
//searchProperties effect
useEffect(() => {
console.log(`App -> effect [searchProperties]`);
setSearchingProperties(true);
setSearchPropertiesError(undefined);
searchProperties(propertiesFilter)
.then(({ error }) => {
if (error) {
setSearchPropertiesError(error);
}
})
.finally(() => {
setSearchingProperties(false);
});
}, [propertiesFilter, searchProperties]);
And renders the data by
{properties.map((property) => (
<Marker
key={property.id}
position={property.coordinates}
icon={getIconForProperty(property)}
>
<Popup>
{property.id}: {property.address}
<button
type="button"
onClick={() => openViewPropertyModal(property.id)}
>
Open details
</button>
</Popup>
</Marker>
))}
The logs are
Warning: The result of getSnapshot should be cached to avoid an infinite loop at App (http://localhost:5173/src/App.tsx?t=1737126342124:33:25)
App -> effect [searchProperties]
propertyStore -> searchProperties {"deleted":false}
realtorMapService -> getProperties
App -> effect [searchProperties]
propertyStore -> searchProperties {"deleted":false}
realtorMapService -> getProperties
(the effect seems to execute twice before the error)
Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
what is causing the infinite loop?