Giter Site home page Giter Site logo

tom-julux / shared-zustand Goto Github PK

View Code? Open in Web Editor NEW
124.0 2.0 4.0 78 KB

cross-tab state sharing for zustand

Home Page: https://www.npmjs.com/package/shared-zustand

License: MIT License

HTML 14.29% TypeScript 40.82% JavaScript 44.90%
js zustand broadcast-channel pwa

shared-zustand's Introduction

sharedzustand

  • ๐Ÿ™ <1.5kB cross-tab state sharing for zustand
  • solid reliability in 1 writing and n reading tab-scenarios (with changing writing tab)
  • Fire and forget approach of always using the latest state. Perfect for single user systems
npm install shared-zustand

or

yarn add shared-zustand

Usage (as of zustand version 4)

import { createStore } from "zustand";
import { subscribeWithSelector} from "zustand/middleware";
import { share, isSupported } from "shared-zustand";

// Create any zustand store
const useStore = createStore(subscribeWithSelector((set) => ({ count: 1 })));

// progressive enhancement check.
if ("BroadcastChannel" in globalThis /* || isSupported() */) {
    // share the property "count" of the state with other tabs
    share("count", useStore);
}

// ...

// somewhere in an event handler
useStore.setState((count) => ({ count: count + 1 }));

Note that I was not able to figure out what the new type of the store is, so I removed the typing from the parameters to the share function. Feel free to make a PR if you do.

Dealing the the deprecation warning in zustand version 3

In new versions of zustand the old selector API is deprecated. Sadly this API is fundamental for this package, as it allows for syncing acros tabs to only occur when a synced property of the store changes.

Luckily zustand ships with a new middleware to restore the selector functionality.

import { create } from "zustand/vanilla";
import { subscribeWithSelector } from 'zustand/middleware'
import { share, isSupported } from "shared-zustand";

const useStore = create(subscribeWithSelector((set) => ({ count: 1 })));

In the future, it may be reasonable to change the behavior of this package to not sync only some properties, but all properties of a given store. This however would, unfortunately, be fully not backward compatible and force users to restructure their data storage models.

Original usage (as of zustand versions 1-2)

import { create } from "zustand/vanilla";
import { share, isSupported } from "shared-zustand";

// Create any zustand store
const useStore = create((set) => ({ count: 1 }));

// progressive enhancement check.
if ("BroadcastChannel" in globalThis /* || isSupported() */) {
    // share the property "count" of the state with other tabs
    share("count", useStore);
}

API (has never changed)

share("count", useStore, {
    // if set to true this tab trys to immediately recover the shared state from another tab.
    initialize: true,
    /*
        Each shared property is shared over a specific channel with an name that has to be unique.
        By default the name of the property is used. So if you want to share properties from different
        stores with the same name, set this to something unique.
    */
    ref: "shared-store",
});

Limitations

  • Only JSON-serilizable objects can be shared

shared-zustand's People

Contributors

danieladarve avatar hansottowirtz avatar tom-julux avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

shared-zustand's Issues

Change isSupported to support non-browser environments

Since ECMAScript (aka Javascript) was designed to run on different platforms, I think it's very primitive to use if BroadcastChannel in window in your code especially since other platforms like React Native may expose the feature under a different name,

First of all, Deno supports BroadcastChannel natively so a better approach would be using globalThis instead of window (only browsers call the global object window, I believe Deno calls itDeno). On browsers globalThis points to window anyway.

I don't know about NodeJS yet but I think BroadcastChannel is exported from the worker_threads module and I think there is a polyfill but I don't think NodeJS support is needed anyways.

DataCloneError: Failed to execute 'postMessage' on 'BroadcastChannel'

Hello,

Im trying to use this library using slices, but it is returning a Runtime error:

DataCloneError: Failed to execute 'postMessage' on 'BroadcastChannel': function() {
let show = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : null;
...... } could not be cloned.

Here is my implementation:

import { isSupported, share } from "shared-zustand"
import create, { StateCreator } from "zustand"
import { subscribeWithSelector } from "zustand/middleware"
import { immer } from "zustand/middleware/immer"

interface SliceA {
    countA: number
}

interface SliceB {
    countB: number
}

type StoreState = SliceA & SliceB

type ImmerType<T> = StateCreator<
    StoreState,
    [["zustand/immer", never], ["zustand/subscribeWithSelector", never]],
    [],
    T
>

const createSliceA: ImmerType<SliceA> = (set) => ({
    countA: 0
})

const createSliceB: ImmerType<SliceB> = (set) => ({
    countB: 0
})

const useStore = create<StoreState>()(
    subscribeWithSelector(
        immer((...a) => ({
            ...createSliceA(...a),
            ...createSliceB(...a),
        })),
    ),
)

if (isSupported()) {
    share("countA", useStore)
}

Any ideas how to solve for slices? @Tom-Julux

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.