Skip to content
This repository was archived by the owner on Jan 27, 2021. It is now read-only.

Commit 083c5d7

Browse files
committed
Initial commit
0 parents  commit 083c5d7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+37947
-0
lines changed

.eslintignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
lib/
2+
node_modules/
3+
.nyc_output/
4+
coverage/
5+
webpack.config.js

.eslintrc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"node": true,
5+
"mocha": true,
6+
"es6": true
7+
},
8+
"parserOptions": {
9+
"ecmaVersion": 6
10+
},
11+
"globals": {
12+
"assert": true,
13+
"expect": true,
14+
"sinon": true
15+
},
16+
"parser": "babel-eslint",
17+
"plugins": [
18+
"react"
19+
],
20+
"extends": ["eslint:recommended", "plugin:react/recommended"],
21+
"rules": {
22+
"no-console": ["warn", { "allow": ["assert"] }],
23+
"react/prop-types": ["error", { "skipUndeclared": true }]
24+
}
25+
}

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules
2+
npm-debug.log
3+
lerna-debug.log
4+
.nyc_output
5+
coverage
6+
.vscode
7+
.idea
8+
lib/

.npmignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.git/
2+
node_modules
3+
npm-debug.log
4+
test/
5+
docs/
6+
examples/
7+
.nyc_output
8+
coverage
9+
.vscode
10+
.idea

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
language: node_js
2+
node_js:
3+
- "8"
4+
script:
5+
- npm run bootstrap
6+
- npm run test

CONTRIBUTING.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Contributing to redux-dynamic-reducer
2+
3+
The following is a set of guidelines for contributing to redux-dynamic-reducer, which are hosted in the [IOOF Holdings Limited Organization](https://github.com/ioof-holdings) on GitHub.
4+
These are just guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a [pull request](#pull-requests).
5+
6+
#### Table of Contents
7+
8+
* [How Can I Contribute?](#how-can-i-contribute)
9+
* [Reporting Bugs](#reporting-bugs)
10+
* [Suggesting Enhancements](#suggesting-enhancements)
11+
* [Pull Requests](#pull-requests)
12+
13+
## How Can I Contribute?
14+
15+
### Reporting Bugs
16+
17+
Bugs are tracked as [GitHub issues](https://github.com/ioof-holdings/redux-dynamic-reducer/issues). Please check to see if your issue has already been raised before submitting your bug report.
18+
19+
When submitting a bug report, please include as much information as possible to help contributors to identify the cause of the problem. The ideal bug report would include:
20+
21+
* **A clear and descriptive title** for the issue to identify the problem.
22+
* **A description of the exact scenario to reproduce the problem** in as much detail as possible.
23+
* **An explanation of the behaviour you expected to see instead and why.**
24+
* **An example to demonstrate the scenario** such as:
25+
* Include copy/pastable snippets (using markdown) in your bug report.
26+
* Links to files in GitHub or other public repository.
27+
* Submit a pull request with [an example](/examples) highlighting the issue.
28+
* **Environment details** such as:
29+
* Browser(s) you have seen the problem in.
30+
* Version of redux-dynamic-reducer you are using.
31+
* Which Redux middleware (including version numbers) are being used.
32+
* What other packages (including version numbers) are being used.
33+
34+
### Suggesting Enhancements
35+
36+
Enhancements are tracked as [GitHub issues](https://github.com/ioof-holdings/redux-dynamic-reducer/issues). Please check to see if your suggestion has already been made before submitting your suggestion.
37+
38+
When submitting an enhancement submission, please include as much information as possible to help contributors to understand an implement your idea. The ideal enhancement suggestion would include:
39+
40+
* **Use a clear and descriptive title** for the issue to identify the suggestion.
41+
* **A description of the specifics of your suggestion** in as much detail as possible.
42+
* **An explanation of the benefits implementing your enhancement would provide**
43+
* **An example to demonstrate the scenario** such as:
44+
* Include copy/pastable snippets (using markdown) in your enhancement submission.
45+
* Links to files in GitHub or other public repository.
46+
* Submit a pull request with [an example](/examples) showing how your enhancement would be used.
47+
48+
### Pull Requests
49+
50+
If you want to get your hands dirty, please take a look at the [open issues](https://github.com/ioof-holdings/redux-dynamic-reducer/issues?q=is%3Aissue%20is%3Aopen) and submit a pull request with your proposed solution, [referencing the issue](https://help.github.com/articles/closing-issues-via-commit-messages/) in commit message.

LICENSE.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2017, IOOF Holdings Limited
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
11+
12+
* Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
* Neither the name IOOF nor the names of its contributors may be used to
17+
endorse or promote products derived from this software without specific
18+
prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# redux-dynamic-reducer
2+
3+
[![npm version](https://img.shields.io/npm/v/redux-dynamic-reducer.svg?style=flat-square)](https://www.npmjs.com/package/redux-dynamic-reducer)
4+
[![npm downloads](https://img.shields.io/npm/dm/redux-dynamic-reducer.svg?style=flat-square)](https://www.npmjs.com/package/redux-dynamic-reducer)
5+
[![License: MIT](https://img.shields.io/npm/l/redux-dynamic-reducer.svg?style=flat-square)](/LICENSE.md)
6+
7+
Use this library to attach additional reducer functions to an existing [Redux](http://redux.js.org/) store at runtime.
8+
9+
This solution is based on an example proposed by Dan Abramov in a [StackOverflow answer](http://stackoverflow.com/questions/32968016/how-to-dynamically-load-reducers-for-code-splitting-in-a-redux-application#33044701).
10+
11+
## Why this library?
12+
13+
A Redux store's state tree is created from a single reducer function. [`combineReducers`](http://redux.js.org/docs/api/combineReducers.html) is the mechanism to compose many reducer functions into a single reducer that can be used to build a hierarchical the state tree. It is not possible to modify the reducer function after the store has been initialised.
14+
15+
This library allows you to attach new reducer functions after the store is initialised. This is helpful if you want to use a single global store across a lazily loaded application where not all reducers are available at store creation. It also provides a convenience functionality that pairs with [redux-subspace](https://github.com/ioof-holdings/redux-subspace) and allows combining a React component with a reducer that automatically attaches to the store when the component is mounted.
16+
17+
## The common use case
18+
19+
This library will help if you want to lazily load and execute pieces of your application but still manage your state in a global store. You can initialise the store in your first page load and efficiently load a skeleton app while the rest of your app is pulled down and loaded asynchronously.
20+
21+
This library pairs well with [redux-subspace](https://github.com/ioof-holdings/redux-subspace) for building complex single-page-applications composed of many decoupled micro frontends.
22+
23+
## Packages
24+
25+
* [`redux-dynamic-reducer`](/packages/redux-dynamic-reducer): The core package for `redux-dynamic-reducer`
26+
* [`react-redux-dynamic-reducer`](/packages/redux-dynamic-reducer): React bindings for `redux-dynamic-reducer`
27+
28+
## How to use
29+
30+
### 1. Create the store
31+
32+
The `createStore` function replaces the [Redux `createStore` function](http://redux.js.org/docs/api/createStore.html). It adds the `attachReducers()` function to the store object. It also supports all the built in optional parameters:
33+
34+
```javascript
35+
import { combineReducers } from 'redux'
36+
import { createStore } from 'redux-dynamic-reducer'
37+
38+
...
39+
40+
const reducer = combineReducers({ staticReducer1, staticReducer2 })
41+
const store = createStore(reducer)
42+
```
43+
44+
```javascript
45+
const store = createStore(reducer, { initial: 'state' })
46+
```
47+
48+
```javascript
49+
const store = createStore(reducer, applyMiddleware(middleware))
50+
```
51+
52+
```javascript
53+
const store = createStore(reducer, { initial: 'state' }, applyMiddleware(middleware))
54+
```
55+
56+
### 2. Dynamically attach a reducer
57+
58+
#### Not using redux-subspace
59+
60+
Call `attachReducers` on the store with your dynamic reducers to attach them to the store at runtime:
61+
62+
```javascript
63+
store.attachReducers({ dynamicReducer })
64+
```
65+
66+
Multiple reducers can be attached as well:
67+
68+
```javascript
69+
store.attachReducers({ dynamicReducer1, dynamicReducer2 })
70+
```
71+
72+
#### When using React and redux-subspace
73+
74+
First, wrap the component with `withReducer`:
75+
76+
```javascript
77+
// in child component
78+
import { withReducer } from 'react-redux-dynamic-reducer
79+
80+
export default withReducer(myReducer, 'defaultKey')(MyComponent)
81+
```
82+
83+
The `withReducer` higher-order component (HOC) bundles a reducer with a React component. `defaultKey` is used by redux-subspace to subspace the default instance of this component.
84+
85+
Mount your component somewhere inside a react-redux `Provider`:
86+
87+
```javascript
88+
// in parent app/component
89+
import MyComponent from './MyComponent'
90+
91+
<Provider store={store}>
92+
...
93+
<MyComponent />
94+
...
95+
</Provider>
96+
```
97+
98+
When the component is mounted, the reducer will be automatically attached to the Provided Redux store. It will also mount the component within a [subspace](https://github.com/ioof-holdings/redux-subspace) using the default key.
99+
100+
Multiple instances of the same component can be added by overriding the default subspace key with an instance specific key:
101+
102+
```javascript
103+
// in parent app/component
104+
import MyComponent from './MyComponent'
105+
106+
...
107+
108+
const MyComponent1 = MyComponent.createInstance('myInstance1')
109+
const MyComponent2 = MyComponent.createInstance('myInstance2')
110+
111+
...
112+
113+
<Provider store={store}>
114+
<MyComponent1 />
115+
<MyComponent2 />
116+
</Provider>
117+
```
118+
119+
Additional state can be mapped for the component or an instance of the component by providing an additional mapper:
120+
121+
```javascript
122+
export default withReducer(myReducer, 'defaultKey', { mapExtraState: (state, rootState) => ({ /* ... */ }) })(MyComponent)
123+
124+
...
125+
126+
const MyComponentInstance = MyComponent
127+
.createInstance('instance')
128+
.withExtraState((state, rootState) => ({ /* ... */ }) })
129+
130+
...
131+
132+
const MyComponentInstance = MyComponent
133+
.createInstance('instance')
134+
.withOptions({ mapExtraState: (state, rootState) => ({ /* ... */ }) })
135+
```
136+
137+
The extra state is merged with the bundled reducer's state.
138+
139+
By default, the components are [namespaced](https://github.com/ioof-holdings/redux-subspace#namespacing). If namespacing is not wanted for a component or and instance of the component, an options object can be provided to prevent it:
140+
141+
```javascript
142+
export default withReducer(myReducer, 'defaultKey', { namespaceActions: false })(MyComponent)
143+
144+
...
145+
146+
const MyComponentInstance = MyComponent.createInstance('instance').withOptions({ namespaceActions: false })
147+
```
148+
149+
## Examples
150+
151+
Examples can be found [here](/examples).
152+
153+
## Limitations
154+
155+
* Each dynamic reducer needs a unique key
156+
* If the same key is used, the last component to use it wins
157+
* Currently, reducers are only ever attached at the root of the store. Nesting is a complex problem we are working on. Components can nested as deep as required, but the store state tree will not match the nesting structure. Consequently, the subspace keys must be unique across the board.

examples/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Examples
2+
3+
redux-dynamic-store is distributed with a few examples in its [source code](/examples). Most of these examples are also on [CodeSandbox](https://codesandbox.io/), this is an online editor that lets you play with the examples online.
4+
5+
## redux-dynamic-reducer
6+
7+
* [Counter](/examples/counter)
8+
9+
## react-redux-dynamic-reducer
10+
11+
* [Todos](/examples/todos)

examples/counter/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Counter Example
2+
3+
This example extends [Redux's counter example](https://github.com/reactjs/redux/tree/master/examples/counter) to attach reducer after store creation.
4+
5+
To run the example locally:
6+
7+
```sh
8+
git clone https://github.com/ioof-holdings/redux-dynamic-reducer.git
9+
10+
cd redux-dynamic-reducer/examples/counter
11+
npm install
12+
npm start
13+
```
14+
15+
Or check out the [sandbox](https://codesandbox.io/s/github/ioof-holdings/redux-dynamic-reducer/tree/master/examples/counter).

0 commit comments

Comments
 (0)