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

electron-shared-state-react

v0.1.2

Published

Share state between electron main and multiple renderer processes

Downloads

2

Readme

Electron Shared State with React

Share state between Electron renderers and main processes

Sharing state between multiple renderers shouldn't be hard. So we fixed it by adding simple React hooks similar to useState.

Example:

export function Settings() {
  const [settings, setSettings] = useSettings('test', [])
  const add = () => setSettings([...settings, 'entry'])

  return (
    <div>
      <button onClick={add}>Add</button>
      {settings.map((s, i) => (
        <div key={`entry-${i}`}>{s}</div>
      ))}
    </div>
  )
}

Getting Started

First, install via yarn or npm npm install electron-shared-state-react to your electron application.

1. Initialize the manager in your Electron main process

There are two sets of managers:

  1. SettingsManager

    With this manager you can persist to disk a set of settings. This is great to store application state.

  2. GlobalStateManager

    With this manager you can syncronize runtime state between the renderer and main processes, but this data will not be persisted.

Ensure that you also dispose these properly at the end of your application lifecycle in your electron main process.

import {
  GlobalSharedStateManager,
  SettingsManager,
} from 'electron-shared-state-react/dist/main'

app.on('ready', async () => {
  ...
  await SettingsManager.ready()
  GlobalSharedStateManager.ready()
  ...
}

app.on('before-quit', async () => {
  ...
  SettingsManager.quit()
  GlobalSharedStateManager.quit()
  ...
}

2. Expose the ipc bridge in your preload.ts for all renderers

We need to expose three methods via Electron's content bridge, so we can communicate with our main process.

preload.ts Example

import { contextBridge, ipcRenderer } from 'electron'
import {
  Platform,
  StateChannel,
} from 'electron-shared-state-react/dist/renderer/platform'

function getStateForChannel<T>(
  channel: StateChannel,
  key: string,
): Promise<{ value: T; hasKey: boolean }> {
  return ipcRenderer.invoke(`${channel}:getState`, key)
}

function setStateForChannel<T>(
  channel: StateChannel,
  key: string,
  value: T | undefined,
  source: string | undefined,
): Promise<void> {
  return ipcRenderer.invoke(`${channel}:setState`, key, value, source)
}

function subscribeToKey<T>(
  channel: StateChannel,
  key: string,
  updatedValue: (\_: any, \_value: T) => void,
): () => void {
  ipcRenderer.addListener(
    `${channel}:updatedValue:${key}`,
    updatedValue
  )
  ipcRenderer.send(`${channel}:watchKey`, key)

  getStateForChannel(channel, key).then((result) => {
    updatedValue(key, result.value as T)
  })

  return () => {
    ipcRenderer.removeListener(
      `${channel}:updatedValue:${key}`,
      updatedValue
    )
    ipcRenderer.send(`${channel}:unwatchKey`, key)
  }
}

const rendererPlatform: Platform = {
  setStateForChannel,
  subscribeToKey,
  getStateForChannel,
}

declare global {
  interface Window {
    // eslint-disable-next-line @typescript-eslint/ban-types
    electronAPI: {
      rendererPlatform: Platform
    }
  }
}

contextBridge.exposeInMainWorld('electronAPI', {
  rendererPlatform,
})

3. Initialize the platform now on all renderers

In order for our Electron renderer hooks and methods to work we need to first initialize the platform we exposed in the previous step.

Put this code anywhere in your renderer entry point (e.g. renderer.ts):

import { initializePlatform } from 'electron-shared-state-react/dist/renderer'
initializePlatform(window.electronAPI.rendererPlatform)

Use it anywhere in your React hooks

You can now use the settings or global state similar to a useState hook in your application:

import { useGlobalState } from 'electron-shared-state-react/dist/renderer/useGlobalState'

export function NumberIncrementer() {
  const [numberOfTasks, setNumberOfTasks] = useGlobalState('numberOfTasks', 0)
  return (
    <div>
      <button onClick={() => setNumberOfTasks(numberOfTasks + 1)}>
        Add Task
      </button>

      <div>{numberOfTasks} Tasks</div>
    </div>
  )
}

Use it outside of React

For settings use:

import {
  fetchSettings,
  setSetting,
} from 'electron-shared-state-react/dist/renderer/useSettings'

// fetch a setting
fetchSettings<string[]>('test').then((settings) => {
  console.log(settings)
})

// set a setting
await setSetting('test', ['test'])

For global state use:

import {
  fetchGlobalState,
  setGlobalState,
} from 'electron-shared-state-react/dist/renderer/useGlobalState'

// fetching a global state
fetchGlobalState<string[]>('test').then((test) => {
  console.log(test)
})

// setting a global state
await setGlobalState('test', ['test'])