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

react-local-reducer

v3.1.0

Published

Use reducers locally in your React app, works with Redux

Downloads

1,678

Readme

react-local-reducer

Build Status npm version styled with prettier

A tiny library (~1k gzipped) to use redux-style reducers locally in your React components. No external dependency, it can be used with or without a redux store.

npm install --save react-local-reducer
# or
yarn add react-local-reducer

For using hooks, see react-hooked-reducer.

Why?

As you scale your Redux application, your store will grow. Over time, as you add more and more reducers to the store, performance will decrease and bundle size will increase.

With react-local-reducer, you can split your store into smaller chunks, and isolate their performance impact. Each chunk is located within the section of your app which depends on it - perfect for code-splitting.

Tell Me More

One root cause of some of the problems associated with a large Redux store is that reducers need to be imported at store creation. This means they cannot be code-split in vanilla Redux, and while there are solutions which allow you to add or remove reducers dynamically, these solutions have trade-offs.

Scaling a redux application means adding more and more reducers to your global store, most of which will end up being irrelevant to the components which are mounted at any given time. Reducers are a bit like singletons, and complexity is added when they relate to a specific component (which isn't a singleton):

  • You end up having to manage initialisation or reset actions
  • Having multiple instances of a same component adds the overhead add and remove actions, forces you to identify the origin of an action and to add more logic in your reducer.

Another issue is that all reducers are called with every action dispatched to the store. If you have a huge number of reducers, this means each action will trigger a huge number of reducers - even the ones which are irrelevant to that action.

Not everything needs to be in a global store, especially state which is not shared across your application, and which strongly relates to a single component. When a reducer is coupled to a component, it can consume global state and global actions, but its state and actions no longer weigh down the store. Emerging architectures like Apollo Client consist of a redux store to cache network data (entities), with the rest of your state managed locally.

React local reducers

  • This library offers a similar solution in React alone: local state which colocates reducers with their components
  • Reducers are created using props and context so they can be initialised, and they must return an object
  • Reducers can receive actions from your main store, with the help of a store enhancer provided with this package

withReducer(reducerCreator, mapDispatchToProps, mergeProps?, contextType?)(BaseComponent)

withReducer is a higher-order component which adds a reducer to a component. Its API is similar to connect, with mapStateToProps being replaced by a reducer. By default, it will spread the output of its reducer to props, alongside action creators bound to your Redux store. Selectors are not necessary.

import React from 'react'
import { withReducer } from 'react-local-reducer'

// Reducer
const reducerFactory = ({ initialCount }) =>
  (state = { count: initialCount }, action) => {
    if (!action) return state

    if (action.type === 'ADD') {
      return ({
        count: state.count + 1
      })
    }

    if (action.type === 'REMOVE') {
      return ({
        count: state.count - 1
      })
    }

    return state
  }

// Action creators
const add = () => ({ type: 'ADD' })
const remove = () => ({ type: 'REMOVE' })

// Component
const Counter = ({ count, add, remove }) => (
  <div>
    Count: { count }
    <button onClick={ add }>Plus</button>
    <button onClick={ remove }>Remove</button>
  </div>
)

export default withReducer(
  reducerFactory,
  { add, remove }
)(Counter)

The above created component would be used as follow:

<Counter initialCount={ 10 } />

One benefit of this approach is that you can have as many counters as you want, without adding complexity to your store.

mergeProps

You can customise how props, state and actionCreators are passed through to your base component by adding a mergeProps function.

This function has the signature (props, state, actionCreators) => ({ /* ... */ }), and the returned object will be passed to the base component as props.

For example, if you wish to pass your state as a single object:

(props, state, actionCreators) => ({ ...props, state, ...actionCreators })

Or if you wish for the separate props to be available in addition to being combined as a single object:

(props, state, actionCreators) => ({ ...props, state, ...state, ...actionCreators })

By default, props, state and actionCreators are simply merged together.

setContextType

You can pass your store and other dependencies using setContextType(context: ReactContext) or the last argument of withReducer.

import { setContextType } from 'react-local-reducer'

setContextType(DependencyContext)

Don't forget to set its value with a its provider. When using withReducer, you need to define a reducer creator (factory).

const reducerCreator = (props, context) => { /* return reducer */ }

onDispatchStoreEnhancer

You can have your local reducers receive any action your global redux store receives. Simply use the redux store enhancer onDispatchStoreEnhancer.

import { createStore } from 'redux'
import { onDispatchStoreEnhancer } from 'react-local-reducer'

const store = createStore(reducer, initialState, onDispatchStoreEnhancer)

For composing multiple store enhancers, look at: http://redux.js.org/docs/api/compose.html