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

redux-ui-state

v3.0.0

Published

UI state management for Redux applications

Downloads

93

Readme

Redux UI State

version licence build gzip size size downloads

UI state management for Redux applications.

Makes storing UI state in the Redux store and sharing it between components simple, safe and transparent.

WARNING: This should only be used for storing small amounts of UI data. Almost all of your application's data should be managed using standard reducers.

Installation

npm install redux-ui-state

Redux UI State is written in TypeScript, so the typings are automatically included and always up to date 🎉

Getting started

NOTE: This is a simple implementation. For more complex implementations (e.g. custom Redux state shape and a dynamic config id), see the example folder.

1. Create the reducer for your app:

Your root reducer should look this:

// rootReducer.js

import { combineReducers } from 'redux';
import { createReducer, DEFAULT_BRANCH_NAME } from 'redux-ui-state';

const initialState = {
  counter: {
    index: 0
  },
}

export default combineReducers({
  ...
  [DEFAULT_BRANCH_NAME]: createReducer(initialState),
  ...
});

2. Use the data in your UI:

Render Prop implementation

Use the setupCreateUIState higher order component to inject uiState (to be used in place of this.state) and setUIState (to be used in place of this.setState) into your render prop.

//////////////////////////////////////////////////
// UIState.js

// This setupCcreateUIState higher order function allows a custom selector for the ui branch of the
// redux store to be passed in if the ui branch is not named 'ui' and at the root. The call to 
// setupConnectUIState should only be done once in your application and the connection function
// should be exported.
const createUIState = setupConnectUIState();

//////////////////////////////////////////////////
// CounterUIState.js

// Either create a render prop component that maps the data into nicely structured props (preferred)
export const CounterUIStateMapped = createUIState(
  'counter',
  ({ uiState, setUIState }) => ({
    index: uiState.index,
    increment: () => setUIState({ index: uiState.index + 1 }),
    decrement: () => setUIState({ index: uiState.index - 1 })
  })
);

// Or create a render prop component that recieves the raw props
export const CounterUIStateUnmapped = createUIState<UIState>('counter');

//////////////////////////////////////////////////
// App.js
import React from 'react';
import Counter from './Counter';

const App = () => (
  <div>

    <UIStateMapped>
      {({ index, increment, decrement }) => (
        <div>
          <div>Value: {index}</div>
          <div>
            <button onClick={decrement}>-</button>
            <button onClick={increment}>+</button>
          </div>
        </div>
      )}
    </UIStateMapped> 
     
    <UIStateUnmapped>
      {({ uiState: { index }, setUIState }) => (
        <div>
          <div>Value: {index}</div>
          <div>
            <button onClick={() => setUIState({ index: index - 1})}>-</button>
            <button onClick={() => setUIState({ index: index + 1})}>+</button> 
          </div>
        </div>
      )}
    </UIStateUnmapped>
    
  </div>
);

Higner order component implementation

Use the setupConnectUIState higher order component to inject uiState (to be used in place of this.state) and setUIState (to be used in place of this.setState) into your component.

//////////////////////////////////////////////////
// UIState.js

// This setupConnectUIState higher order function allows a custom selector for the ui branch of the
// redux store to be passed in if the ui branch is not named 'ui' and at the root. The call to 
// setupConnectUIState should only be done once in your application and the connection function
// should be exported.
const connectUIState = setupConnectUIState();

//////////////////////////////////////////////////
// Counter.js
import React from 'react';
import { defaultConnectUIState as connectUIState } from 'redux-ui-state';

const Counter = ({ indexMessage, increment, decrement }) => (
  <div>
    <div>
      {indexMessage}
    </div>
    <div>
      <button onClick={decrement}>-</button>
      <button onClick={increment}>+</button>
    </div>
  </div>
);

export default connectUIState(
  'counter',
  ({ index }, { setUIState }, { prefix }) = ({
    indexMessage: `${prefix}${index}`,
    decrement: () => setUIState({ index: index - 1 }),
    increment: () => setUIState({ index: index + 1 }),
  })
)(Counter);

//////////////////////////////////////////////////
// App.js
import React from 'react';
import Counter from './Counter';

const App = () => (
  <div>
    <Counter prefix="Value: " initialIndex={0} />
  </div>
);

Rationale

Shorter practical answer 🔨

Sharing UI state between components usually means either:

  • Adding state to a parent component that shouldn't really be concerned with it and passing it through view layers that also shouldn't be concerned with it.
  • Creating a custom UI state reducer with specific actions for each atom of state that will change.

Redux UI State replaces these overly complex, error prone solutions with a reducer and a higher order component that provides clean access to UI state held in the Redux store.

Longer theoretical answer 🤓

Single state atom

Redux is a predictable, easy to undertand state container primarly because it has a single state object. This allows developers to inspect the entire state of an application at any given time and create powerful dev tools that enable magical things like time travel debugging. Keeping everything in one place is a large part of what drove Redux to win out over the traditional multi store Flux architecture yet most React applications let multiple data stores in through the back door by using this.setState in components.

As soon as this happens the single state atom principle breaks down, the entire application state cannot be inspected and full state serialization / time travel debugging becomes impossible.

Sharing state with siblings

Sibling components sometimes need to share their UI state. This can be achieved by moving that state up into the parent component, but this is often not something the parent should be concerned with, which means increased complexity, confusing APIs, less reusability and the system as a whole being more prone to errors.

Sharing state with children

In situations where it does make sense for a parent to hold a piece of UI state, that data may be needed several layers down in the view hierarchy, meaning intermediate components, unconcerned with the data in question, need to include it in their APIs, again creating unnecessary complexity, which results in confusing APIs and a greater likelihood of errors cropping up.

A solution

Redux UI State replaces these overly complex, error prone solutions with a higher order component that provides clean access to UI state held in the Redux store.

TODO / Roadmap

  • Add Github pages page
  • Add documentation for advanced implementations
  • Add plain JavaScript examples
  • Add flow type definitions