@joystick.js/ui

Global State

Manage global state across Joystick components using the built-in Cache API.

Under the hood, @joystick.js/ui includes a global state library called Cache. Similar to popular libraries like Redux, Cache allows you to create a "store" or "cache" for storing global data. It features a simple API for getting, setting, and unsetting data from a cache as well as listening for changes to a cache.

By default, Joystick initializes a cache for you and makes it accessible as a named export at global_state (and an alias on the default joystick export at joystick.global_state).

Example Usage

ui/pages/store/index.js

import joystick, { global_state } from "@joystick.js/ui";

import Cart from '../../components/cart/index.js';

const Store = joystick.component({
  events: {
    'click [data-item-id]': (event = {}, instance = {}) => {
      global_state.set((state = {}) => {
        return {
          ...state,
          cart: [
            ...state.cart || [],
            { id: event.target.getAttribute('data-item-id') }
          ],
        };
      });
    },
  },
  render: ({ component }) => {
    return `
      <div>
        <div class="store">
          <button data-item-id="book">Add a Book to Cart</button>
          <button data-item-id="t-shirt">Add a T-Shirt to Cart</button>
          <button data-item-id="apple">Add an Apple to Cart</button>
        </div>
        ${component(Cart)}
      </div>
    `;
  },
});

export default Store;

In this example, we add items to the global state on button click using global_state.set(). The updated state is stored globally and can be accessed or modified from any component.

ui/components/cart/index.js

import joystick, { global_state } from "@joystick.js/ui";

const Cart = joystick.component({
  state: {
    cart: [],
  },
  lifecycle: {
    on_mount: (instance = {}) => {
      global_state.on('change', (state = {}, event = '', user_event_label = '') => {
        instance.set_state({ cart: state?.cart });
      });
    }
  },
  render: ({ state, each, when }) => {
    return `
      <div>
        <div class="items">
          ${when(state?.cart?.length === 0, `
            <p>No items in cart</p>
          `)}
          ${when(state?.cart?.length > 0, `
            <ul>
              ${each(state?.cart, (item) => {
                return `<li>${item.id} <button>X</button></li>`;
              })}
            </ul>
          `)}
        </div>
      </div>
    `;
  },
});

export default Cart;

Here, the Cart component listens for changes to the global state using global_state.on() and syncs the global cart state with its local component state.

API

Definition

global_state: {
  get: (path: string) => object,
  on: (
    event_type: string,
    callback: (existing_state: object, event: string, user_event_label: string) => void
  ) => void,
  set: (
    callback: (existing_state: object) => object,
    user_event_label: string
  ) => void,
  unset: (path: string, user_event_label: string) => void,
}

Parameters

get function required
Retrieve the current global state value. Optionally provide a path string (e.g., 'cart' or 'user.profile') to access a nested value.
on function required
Listen for changes to the global state.
event_type string required
The type of event to listen for: 'set', 'unset', or 'change'.
callback function required
A callback function called when the event occurs. Receives the current global state, event type, and optional user event label.
set function required
Update the global state by passing a function that returns the updated state object. Optionally include a user event label for debugging.
unset function required
Unset a value from the global state. Provide a path string (e.g., 'cart') to remove a specific property, or call without a path to clear the entire state.