Sync local and remote Zustand store in ReactJS

const useStore = create(set => ({
bears: 0,
nutrition: {
breakfast: ["honey"],
diner: ["nuts"]
}
}))
import { useEffect } from "react"
import shallow from "zustand/shallow"
import produce from "immer"
import get from "lodash.get"
import set from "lodash.set"
import { useStore } from "path/to/your/store"/**
* @param {string[]} syncPaths - Paths of the store you want to sync, e.g. ["nutrition.breakfast", "nutrition.diner"]
* @param {Object} newData - Patched data from remote store
* @param {Function} doSendData - Function will be invoked with patched local store data, up to send to remote
*/
const useStoreSync = (syncPaths, newData, doSendData) => {
/**
* This useEffect will be invoked everytime prop syncPaths change.
* It calls doSendData with currently changed state, where key is path of syncedPaths and value is the current state, e.g.:
* {
* "nutrition.breakfast": ["honey"],
* "nutrition.diner": ["nuts"]
* }
*/
useEffect(() => {
const unsubscribes = syncPaths.map(path => {
return useStore.subscribe(
state => get(state, path), changedPath => {
const sendState = { [path]: changedPath }
doSendData(sendState)
}
)
})
return () => unsubscribes.map(unsubscribe => unsubscribe())
}, [syncPaths])
/**
* This useEffect will be invoked everytime remote data (@param newData) changes.
* The local store will be updated with patch Object, which might be:
* {
* "nutrition.breakfast": ["berries", "honey"],
* }
*/
useEffect(() => {
Object.keys(newData).forEach(key => {
useStore.setState(
produce(state => {
set(state, key, newData[key])
}),
)
})
}, [newData])
}
export default useStoreSync

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store