npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

zedux-sync

v0.0.3

Published

Reactively sync proxied stores across realms

Downloads

11

Readme

Zedux Sync

Reactively sync proxied stores across realms. Yeah.

Note that this package is still under development.

Installation

Install using npm:

npm install zedux-sync

Or yarn:

yarn add zedux-sync

Or include the appropriate unpkg build on your page (module exposed as ZeduxSync):

Development

<script src="https://unpkg.com/zedux-sync/dist/zedux-sync.js"></script>

Production

<script src="https://unpkg.com/zedux-sync/dist/zedux-sync.min.js"></script>

Getting started

To learn by example, check out the examples in the repo.

To learn from us, check out the documentation.

To learn comprehensively, check out the tests.

Or keep reading for a brief run-down:

What's a realm?

A realm is just an isolated execution context. The glowing example here is an iframe. An iframe is an isolated window environment that lives inside another window. Communication between these two realms is governed by a strict set of rules and thus can be a pain to work with.

Realms include workers, browser extensions, iframes, your server, virtual machines, and, of course, the realms proposal.

How does Zedux Sync work?

Zedux Sync takes a couple tips from React.

A single source of truth

Cross-realm applications often have state spattered here and there across the realms. With Zedux Sync, you instead create a single Zedux store in the highest realm to serve as the "single source of truth". Each inner realm then gets a "proxy" store whose state will be synchronized with the almighty source of truth.

Rather than manually wiring events in one realm to state updates in another (e.g. typical ajax call and response patterns), you just dispatch actions to the current realm's proxy. Zedux Sync manages shuttling the actions to the highest store where they will be dispatched normally. Zedux Sync then manages propagating the resulting state change across all registered proxies in all inner realms.

React to state changes

Each realm can subscribe to its proxy store and reactively update UI when Zedux Sync updates the state. For apps built in React, React Zedux handles this subscription for you.

Optimal state updates

Since communications between realms usually have to be serialized by the sender then deserialized by the receiver, it's important to make these communications as small as possible. JSON.parse() and JSON.stringify() are slow! Zedux Sync is able to find the "hot path" of each state update and sends the bare minimum data as a partial hydrate action to the proxied stores of inner realms.

Example Usage

Let's take a simple iframe example. Normally we'd have to do something like the following to manually wire up the communication between the iframe and the parent window:

// in the parent window:
iframe.contentWindow.addEventListener('message', handleIframeMsg)
iframe.contentWindow.postMessage(message, '*')


// in the iframe:
window.addEventListener('message', handleParentMsg)
window.postMessage(message, '*')

This is the old-school event-based model. While simple it is not reactive. Data flows both ways. It does not encourage a single source of truth. This sort interwoven state management can quickly become dizzying. There is a better way.

Zedux Sync allows us to simply create a store in both the parent window and the iframe. The parent window's store will be the real single source of truth. The iframe's store will be a "proxy". All we have to do is hook them up to each other and Zedux Sync will keep them synchronized:

// in the parent window:
import { createStore } from 'zedux'
import { createDomProxy } from 'zedux-sync'

const store = createStore()
const domProxy = createDomProxy(store)

// This is all we need to do to hook up the two stores.
// This assumes "iframe" is a loaded HTMLIFrameElement.
domProxy.addIframe(iframe)
domProxy.setState({ isAwesome: true })


// in the iframe:
import { createIframeProxy } from 'zedux-sync'

const store = createStore()
const iframeProxy = createIframeProxy(store)

iframeProxy.subscribe(newState => {
  console.log('the new state:', newState)

  // as soon as the iframe loads, this will log:
  // the new state: { isAwesome: true }
})

That's it! At this point, any action (or inducer or setState/hydrate call) dispatched to either store will get shuttled up to the reducer layer of the parent window's store. The parent window's store will calculate the new state and notify the attached iframe proxy of the state update:

// in the parent window:
domProxy.subscribe(newState => {
  console.log('the new state:', newState)
})


// in the iframe:
iframeProxy.setState({ isSuperAwesome: true })

After running this, both the parent window and the iframe will log:

the new state: { isAwesome: true, isSuperAwesome: true }

API

Currently, Zedux Sync only supports creating proxies in 4 different realms (plenty more to come!):

  • createChromeRuntimeProxy - Realm: Chrome extension runtime
  • createChromeTabProxy - Realm: Chrome extension tab
  • createDomProxy - Realm: Any normal webpage (client-side)
  • createIframeProxy - Realm: An iframe.

Chrome Runtime proxies and Dom proxies are meant to wrap top-level stores (the "single source of truth" stores). As such, they would never be used together. A reactive Zedux Sync flow for a chrome extension might look like:

              chromeRuntimeProxy
             /                  \
      chromeTabProxy       chromeTabProxy
     /              \
iframeProxy     iframeProxy
     |
iframeProxy

In this example, the Chrome Runtime proxy is the single source of truth and it can sync up with any number of Chrome Tab proxies which in turn can sync up with any number of iframe proxies. Iframe proxies can be nested indefinitely (just as iframes can be nested indefinitely).

Any action/inducer/etc dispatched to any store anywhere in this tree will be relayed up to the Chrome Runtime store. There the state update will be calculated and propagated down the entire tree. :O

More documentation and concrete examples to come on these functions. For now, the only important thing to note is that iframes must be manually added to a parent proxy. Chrome Tab proxies, Dom proxies, and iframe proxies all have two methods:

addIframe

Add an iframe to the proxy. Any iframe proxies in the iframe will begin receiving state updates from and relaying messages up to this proxy automatically. Example:

import { createStore } from 'zedux'
import { createTabProxy } from 'zedux-sync'

const store = createStore()
const proxy = createTabProxy(store)

proxy.addIframe(document.querySelector('iframe'))

removeIframe

Remove the given iframe from the proxy. Any iframe proxies in the iframe will no longer receive state updates from this proxy. Example:

domProxy.removeIframe(document.querySelector('iframe'))

What if my page won't always be loaded in an iframe?

Zedux Sync will detect if an iframe proxy is created outside an iframe. When this happens, the iframe proxy will simply dispatch all received actions/inducers/whatever directly to the wrapped store rather than attempting to shuttle them up to the non-existent parent window's store.