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

@zumper/react-redux-ephemeral-store

v0.2.1

Published

Factory methods for creating an ephemeral redux store bound to a react context and a react-redux provider

Downloads

5

Readme

@zumper/react-redux-ephemeral-store

Factory methods for creating an ephemeral redux store bound to a react context and a react-redux provider.

If you are excited about the possibilities of using the useReducer hook with createContext then this is for you.

Why?

The primary use-case is to create a redux store that is only available to a specific react component tree. It's called an "ephemeral store" because it only lives as long as the <Provider /> is mounted (just like useReducer).

Why use redux + react-redux instead of useReducer + createContext?

We wanted to share state between some components using redux-like code patterns but our global store was not a good fit. Initially we turned to useReducer and createContext. Later we switched to useEnhancedReducer to enable middleware, like redux-thunk.

Next, we recognized that useReducer provided functionality that looked a lot like a redux store. We started providing our fake redux store using createContext but we needed a good way to connect to it from within our component tree. We were able to wrap it up into a store-like object, which was compatible with react-redux.

Since version 6, react-redux has supported an option to connect to a custom context. Rather than reinvent the wheel, we decided to simply use react-redux with our custom context and fake-store. Since version 7.1.1, react-redux allows for binding the provided hooks with a custom context. Now we had hooks!

Finally, we realized that making a fake redux store with useReducer was way harder than just using redux createStore.

Relying on redux and react-redux under the hood provided us with some significant performance gains and allowed us to keep our existing coding patterns.

NOTE: If you want share a store with your entire app, just use react-redux.

Install

yarn add @zumper/react-redux-ephemeral-store

Usage

You can use the createEphemeralStoreProvider to create a version of react-redux that is bound to a custom context and creates a new store every time the Provider is mounted.

It returns a bound version of the core react-redux interface.

import { createEphemeralStoreProvider } from '@zumper/react-redux-ephemeral-store'

export const {
  connect,
  Context,
  Provider,
  useStore,
  useDispatch,
  useSelector,
} = createEphemeralStoreProvider({
  reducer,
  preloadedState,
})

API

createEphemeralStoreProvider(options)

Creates an ephemeral store provider and returns a react-redux interface that is bound to a custom context. The options you provide are used to create the redux store when the Provider is mounted.

options

  • reducer (Function): Passed to redux createStore as the first argument.
  • preloadedState (any): Passed to redux createStore as the second argument
  • enhancer (Function): Passed to redux createStore as the third argument
  • middleware (Array): Passed to redux applyMiddleware to create the redux store enhancer. This option is ignored if the enhancer option is provided.
  • context (Context): Optional; If you do not pass in a context it will be created automatically

enhancer vs. middleware

It is expected that most ephemeral stores will have simple needs and will probably enjoy the convenience of using the middleware option. However, if you have advanced needs you can use the enhancer option to do anything you normally could with redux.


Example

We can create a custom Provider that binds a custom context to react-redux and creates an ephemeral store when it is first mounted.

Step 1: Create a <MyProvider />

The main point here is to create a Provider that makes a reducer available to a component tree. You can use any reducer and preloadedState that you would with useReducer or createStore.

You can see below that we are using the middleware option to apply redux-thunk middleware to our store. You can supply any middleware that is compatible with applyMiddleware.

NOTE: You should rename the connectFoo and FooProvider variables to suit your needs. You can name them whatever you please. We chose My in these examples for simplicity.

// myStoreProvider.js
import { createEphemeralStoreProvider } from '@zumper/react-redux-ephemeral-store'
import thunk from 'redux-thunk'

import { reducer, preloadedState } from './module'

export const {
  connect: connectMy,
  Context: MyContext,
  Provider: MyProvider,
  useStore: useMyStore,
  useDispatch: useMyDispatch,
  useSelector: useMySelector,
} = createEphemeralStoreProvider({
  reducer,
  preloadedState,
  middleware: [thunk],
})

Step 2: Wrap a component tree with your <MyProvider />

Then we can provide access to the store to our component tree.

// MySection.jsx
import React from 'react'
import { MyProvider } from './myStoreProvider'
import { DeepContainer } from './DeepContainer'

export const MySection = () => {
  return (
    <MyProvider>
      <DeepContainer />
    </MyProvider>
  )
}

Step 3: Use connectMy to interface with your ephemeral store

Finally, we can connect our deeply nested components to the ephemeral store.

// DeepContainer.js
import { connectMy } from './myStoreProvider'
import { Deep } from './Deep'

const mapMyStateToProps = (state) => {
  return {
    foo: state.foo,
  }
}

const mapMyDispatchToProps = (dispatch) => {
  return {
    onClick: () => dispatch({ type: 'FOO', payload: 'bar' }),
  }
}

export const DeepContainer = connectMy(mapMyStateToProps, mapMyDispatchToProps)(
  Deep
)

Using hooks

React-redux now provides a hooks interface. Starting from version 7.1.1 it is possible to bind react-redux hooks to a custom context.

Below you can see a simple example of using the custom useMyStore hook we created above.

Read more about using hooks with react-redux.

import React, { useContext } from 'react'
import { useMyStore } from './myStoreProvider'

export const Deep = () => {
  const myStore = useMyStore()
  const state = myStore.getState()
  const value = state.foo
  return <div>{value}</div>
}

Bonus: mixing with global react-redux

You can also mix connect with your custom connectMy.

// DeepContainer.js
import { compose } from 'redux'
import { connect } from 'react-redux'
import { connectMy } from './myStoreProvider'
import { Deep } from './Deep'

// connects to global redux store
const mapStateToProps = (state) => {
  return {
    bar: state.bar,
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onClick: () => dispatch({ type: 'BAR', payload: 'baz' }),
  }
}

// connects to ephemeral store
const mapMyStateToProps = (state) => {
  return {
    foo: state.foo,
  }
}

const mapMyDispatchToProps = (dispatch, ownProps) => {
  return {
    onClick: (event) => {
      // we can intercept and re-bind props
      ownProps.onClick(event)
      dispatch({ type: 'FOO', payload: 'bar' })
    },
  }
}

export const DeepContainer = compose(
  connect(
    mapMyStateToProps,
    mapMyDispatchToProps
  ),
  connectMy(mapMyStateToProps, mapMyDispatchToProps)
)(Deep)