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-redux-with-path

v0.0.6

Published

A version of react-redux designed for cases that want to access sub-tree of the state. and take it as state in mapStateToProps and mapDispatchToProps

Downloads

9,264

Readme

react-redux-with-path

Problems

When you start to build a large react app (we call it main app) which may consist of multiple smaller apps (we call them component apps), you will possibly get stuck with some problems such as:

  • The integration of the main app and its component apps should be as little as possible. Ideally, we can develop them separately and the component apps are pluggable.
  • Component apps shouldn't be aware of the existence of each other and the main app either. For example, the states of other apps and the main app should be invisible to any component app.

Specifically, you may encounter the following problems in development of the main app:

  1. Every component app should keep its own store state in terms of redux. You would prefer to place it in a sub-tree of the main app store state just like a namespace for it.

  2. When using utilities like mapStateToProps, mapDispatchToProps, Provider, connect from react-redux, you have to do something like:

    const mapStateToProps = state => (
        { property: state.app.reducer.property }
    );

    But what you really want is:

    const mapStateToProps = state => (
        { property: state.reducer.property }
    );

    which means the component app take its own state as the global store state.

  3. When the main app have several identical component apps inside. There can be several identical reducers listening to a single action. If it is not intended, you have to find out a way to ask each reducer to listen to the action that is really dispatched to it.

  4. If you want your main app to load component apps conditionally, you probably want to load the view, reducer, redux-saga and do initialisation on demand, instead of required all component apps at the beginning.

If you are facing similar problems above, you probably want to see the following discussion and take a look at this package react-redux-with-path.

Solution

namespace

As discussed above, we need to create namespace for each app. Inspired by How to dynamically load reducers for code splitting in a Redux application. We're able to inject reducers on demand and use combineReducers to create a namespace for each component app and the main app. We can do

function createReducer(reducersOnDemand) {
  return combineReducers({
    mainApp: mainAppReducer,
    ...reducersOnDemand,
  });
}

function injectReducersOnDemand(store, reducerOnDemand) {
  store.reducersOnDemand[path] = reducerOnDemand;
  store.replaceReducer(createReducer(store.reducersOnDemand));
}

Along this way, the hierarchies of both combined reducer and store state become

.
├── componentApp1
│   ├── reducer1
│   ├── reducer2
│   └── reducer3
├── componentApp2
├── componentApp3
└── mainApp

We define the first level directory names (componentApp1, componentApp2...) as the path of each app. The path can be the component app name (which won't work in the case that the main app can have several identical component apps) or an unique app id for each component app instance.

Component App Isolation

As mentioned above, developing each component app as the whole app is a nice-to-have feature. Especially when we use Provider and connect utilities from react-redux. In this package react-redux-with-path, we provide two APIs SubProvider and connect to enable the normal way of developing each component app. See APIs part for details.

APIs

The two apis should be used together.

SubProvider [will be renamed to PathProvider]

As we know, the Provider from react-redux enable you to propagate the dispatch utility and state object from the redux store to any sub-component that is connected to, namely the sub-component wrapped with connect, connect(mapStateToProps, mapDispatchToProps)(SubComponent). In the same way, our SubProvider provides enable you to propagate the path to sub-components. More precisely, the path is provided to our connect API.

import { SubProvider } from 'react-redux-with-path';

const componentAppJSX = (
	<SubProvider path={path}>
		<ComponentApp />
	</SubProvider>
);

connect

After wrapped with SubProvider, our connect api will provide any sub-component of component app with its own state, which is the sub-tree the main app store state. The normal usage:

const mapDispatchToProps = {
	actionA: actionCreatorA,
	actionB: actionCreatorB,
} // don't bindActionCreators here, let the connect api do it for you
const mapStateToProps = state => ({
	propA: state.reducer.propA,
	propB: state.reducer.propB,
});
export default connect(mapStateToProps, mapDispatchToProps)(SubComponent);

Boom! the component app get its own state and it is not aware of other part of the state tree.

Install

npm install react-redux-with-path --save

Notes

Now, this package can only help you namespace each component app, and it works in the case that component apps are not identical. You may worry about the collision of actions with same types from different component apps. But that can be avoided if you follow the ducks-modular-redux. However, there exists problems in more complicated situations. For example, the problem 3, 4 in our Problems section. Inspired by redux issues #1628, our solution to problem 3 and 4 is to wrap the actions of each component app with an additional field called path, and the reducers of each component app with a path check. The former is actually done in the connect api. For the latter, you have to do it yourself like

function wrapReducerWithPathCheck(reducer, path) {
  return (state = {}, action) => {
    if (!isActionFromRightPath(action, path) && !isActionFromRedux(action)) {
      return state;
    }
    let hasChanged = false;
    let previousState;
    if (Object.keys(state).length > 0) {
      previousState = state;
    }
    const nextState = reducer(previousState, action);
    hasChanged = hasChanged || nextState !== previousState;
    return hasChanged ? nextState : state;
  };
}

This wrapReducerWithPathCheck utility is not shipped with the current version of react-redux-with-path. We may add that when it is ready for production or not add that if we find out a more elegant approach.