├── .babelrc
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .npmignore
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── LICENSE.md
├── README.md
├── devtools
├── devServer.js
├── index.ejs
├── webpack.config.config.js
├── webpack.config.dev.js
├── webpack.config.dist.js
├── webpack.config.dist.min.js
├── webpack.config.dlllib.dev.js
└── webpack.config.lib.js
├── jsconfig.json
├── lib
└── simpler-redux.js
├── package.json
├── src
├── index.js
├── proxy.js
├── react-simpler-redux.js
├── simpler-redux.js
└── util.js
└── test
├── App.jsx
├── Counter
├── index.js
├── model.js
└── view.jsx
├── Counter1
├── index.js
├── model.js
└── view.jsx
├── Counter2
├── index.js
├── model.js
└── view.jsx
├── Counter3
├── index.js
├── model.js
└── view.jsx
├── Counter4
├── index.js
├── model.js
└── view.jsx
├── Counter5
├── index.js
├── model.js
└── view.jsx
├── Counter6
├── index.js
├── model.js
└── view.jsx
├── Counter7
├── index.js
├── model.js
└── view.jsx
├── Counter8
├── index.js
├── model.js
└── view.jsx
├── WrapCounter1
├── index.js
├── model.js
└── view.jsx
├── mountapp.jsx
├── projectsetup.js
├── reducers.js
├── reduxstore.js
├── setup.js
├── test.js
├── testApp.js
└── util.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "env",
4 | "react"
5 | ],
6 | "plugins": [
7 | "transform-object-rest-spread",
8 | "react-hot-loader/babel",
9 | "transform-react-jsx"
10 | ],
11 | "env": {
12 | "production": {
13 | "presets": [
14 | "react-optimize"
15 | ]
16 | },
17 | "development": {
18 | "plugins": [
19 | "transform-react-jsx-source"
20 | ]
21 | }
22 | },
23 | "sourceMaps": true,
24 | "retainLines": true
25 | }
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | devdll/**
2 | dist/**
3 | lib/**
4 | temp/**
5 | src/**/*.inject*
6 | src/**/*.scss
7 | src/**/*.sass
8 | src/**/*.less
9 | src/**/*.css
10 | src/assets/**
11 | node_modules/**
12 | .vscode/chrome/**
13 | dllbundle.js
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | "parser": "babel-eslint",
5 | "plugins": [
6 | "babel",
7 | "react"
8 | ],
9 | "settings": {
10 | "import/resolver": {
11 | "webpack": {
12 | "config": path.join(__dirname, "devtools/webpack.config.dev.js")
13 | }
14 | }
15 | },
16 | "parserOptions": {
17 | "ecmaFeatures": {
18 | "jsx": true
19 | },
20 | "sourceType": "module",
21 | "ecmaVersion": 8
22 | },
23 |
24 | "extends": [
25 | "standard",
26 | "standard-react",
27 | "plugin:import/warnings",
28 | "plugin:import/errors"
29 | ],
30 | "env": {
31 | "amd": true,
32 | "browser": true,
33 | "jquery": true,
34 | "node": true,
35 | "es6": true,
36 | "worker": true,
37 | "mocha": true
38 | },
39 | "rules": {
40 | "react/prop-types": 0,
41 | "react/jsx-boolean-value": 0
42 | }
43 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/chrome/**
2 | devdll/**
3 | node_modules/**
4 | temp/**
5 | docs/**
6 | package-lock.json
7 | index.dev.ejs
8 | todo.txt
9 | test.dev/**
10 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | devdll/**
3 | devtools/devServer.js
4 | devtools/index.ejs
5 | node_modules/**
6 | temp/**
7 | docs/**
8 | test.dev/**
9 | .eslintignore
10 | .eslintrc.js
11 | index.dev.ejs
12 | jsconfig.json
13 | package-lock.json
14 | .gitignore
15 | .npmignore
16 | .gitattributes
17 | .babelrc
18 | docs/**
19 | todo.txt
20 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "msjsdiag.debugger-for-chrome",
4 | "dbaeumer.vscode-eslint",
5 | "xabikos.ReactSnippets",
6 | "fknop.vscode-npm",
7 | "christian-kohler.npm-intellisense"
8 | ]
9 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Debug",
6 | "type": "chrome",
7 | "request": "launch",
8 | "webRoot": "${workspaceRoot}",
9 | "url": "http://localhost:3000/index.html",
10 | "userDataDir": "${workspaceRoot}/.vscode/chrome",
11 | "sourceMaps": true,
12 | "smartStep": true,
13 | "preLaunchTask": "Development",
14 | "internalConsoleOptions": "openOnSessionStart",
15 | "skipFiles": [
16 | "node_modules/**"
17 | ],
18 | "sourceMapPathOverrides": {
19 | "webpack:///*": "${webRoot}/*"
20 | }
21 | },
22 | {
23 | "name": "Run Mocha Tests",
24 | "type": "node",
25 | "request": "launch",
26 | "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
27 | "stopOnEntry": false,
28 | "sourceMaps": true,
29 | "smartStep": true,
30 | "args": [
31 | "--require",
32 | "babel-register",
33 | "--require",
34 | "./test/setup.js",
35 | "./test/mountapp.jsx",
36 | "./test/projectsetup.js",
37 | "./test/test.js",
38 | "./test/testApp.js",
39 | "--no-timeouts",
40 | "--colors"
41 | ],
42 | "cwd": "${workspaceRoot}",
43 | "runtimeExecutable": null,
44 | "env": {"NODE_ENV": "testing"}
45 | },
46 | {
47 | "name": "Debug Mocha Tests",
48 | "type": "node",
49 | "request": "launch",
50 | "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
51 | "stopOnEntry": false,
52 | "sourceMaps": true,
53 | "smartStep": true,
54 | "args": [
55 | "--require",
56 | "babel-register",
57 | "--require",
58 | "./test/setup.js",
59 | "./test/mountapp.jsx",
60 | "./test/projectsetup.js",
61 | "./test/test.js",
62 | "./test/testApp.js",
63 | "--no-timeouts",
64 | "--colors"
65 | ],
66 | "cwd": "${workspaceRoot}",
67 | "runtimeExecutable": null,
68 | "env": {"NODE_ENV": "debugTesting"}
69 | }
70 | ]
71 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "editor.formatOnType": true,
4 | "eslint.enable": true,
5 | "files.associations": {
6 | "**/src/**/*.js": "javascriptreact",
7 | "**/node_modules/**/*.js": "javascript",
8 | ".babelrc": "json",
9 | ".eslintignore": "gitignore"
10 | },
11 | "files.watcherExclude": {
12 | ".vscode/chrome/**": true
13 | },
14 | "html.format.extraLiners": "!",
15 | "html.format.indentInnerHtml": true,
16 | "json.schemas": [
17 | {
18 | "fileMatch": [
19 | "/package.json"
20 | ],
21 | "schema": {
22 | "type": "object",
23 | "properties": {
24 | "config": {
25 | "properties": {
26 | "port": {
27 | "type": "number",
28 | "description": "The port of the webpack-dev-server",
29 | "default": 3000
30 | },
31 | "host": {
32 | "type": "string",
33 | "description": "The hostname or ip of the webpack-dev-server",
34 | "default": "localhost"
35 | },
36 | "ghooks": {
37 | "type": "object",
38 | "description": "Git hook configurations",
39 | "properties": {
40 | "applypatch-msg": {
41 | "type": "string"
42 | },
43 | "pre-applypatch": {
44 | "type": "string"
45 | },
46 | "post-applypatch": {
47 | "type": "string"
48 | },
49 | "pre-commit": {
50 | "type": "string"
51 | },
52 | "prepare-commit-msg": {
53 | "type": "string"
54 | },
55 | "commit-msg": {
56 | "type": "string"
57 | },
58 | "post-commit": {
59 | "type": "string"
60 | },
61 | "pre-rebase": {
62 | "type": "string"
63 | },
64 | "post-checkout": {
65 | "type": "string"
66 | },
67 | "post-merge": {
68 | "type": "string"
69 | },
70 | "pre-push": {
71 | "type": "string"
72 | },
73 | "pre-receive": {
74 | "type": "string"
75 | },
76 | "update": {
77 | "type": "string"
78 | },
79 | "post-receive": {
80 | "type": "string"
81 | },
82 | "post-update": {
83 | "type": "string"
84 | },
85 | "pre-auto-gc": {
86 | "type": "string"
87 | },
88 | "post-rewrite": {
89 | "type": "string"
90 | }
91 | }
92 | }
93 | }
94 | }
95 | }
96 | }
97 | }
98 | ],
99 | "search.exclude": {
100 | "dist/**": true,
101 | "lib/**": true,
102 | "temp/**": true,
103 | "node_modules": true,
104 | ".git": true,
105 | ".vscode/chrome/**": true,
106 | "devdll/**": true
107 | },
108 | "files.exclude": {
109 | "**/.git": true,
110 | "**/.DS_Store": true,
111 | "**/node_modules": true,
112 | ".vscode/chrome/**": true
113 | },
114 | "typescript.check.tscVersion": false
115 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | // Available variables which can be used inside of strings.
2 | // ${workspaceRoot}: the root folder of the team
3 | // ${file}: the current opened file
4 | // ${fileBasename}: the current opened file's basename
5 | // ${fileDirname}: the current opened file's dirname
6 | // ${fileExtname}: the current opened file's extension
7 | // ${cwd}: the current working directory of the spawned process
8 | {
9 | "version": "0.1.0",
10 | "command": "npm",
11 | "isShellCommand": true,
12 | "echoCommand": false,
13 | "suppressTaskName": true,
14 | "showOutput": "always",
15 | "tasks": [
16 | {
17 | "args": [
18 | "run",
19 | "start",
20 | "--silent",
21 | "--react-hot-boilerplate-vscode:port=3000",
22 | "--react-hot-boilerplate-vscode:host=localhost" // use these command line args to override host and port of the dev-server. default values are defined in package.json config section.
23 | ],
24 | "problemMatcher": [
25 | {
26 | "owner": "custom",
27 | "pattern": {
28 | "regexp": "____________"
29 | },
30 | "background": {
31 | "activeOnStart": true,
32 | "beginsPattern": "^webpack: Compiling\\.\\.\\.$",
33 | "endsPattern": "^webpack: (Compiled successfully|Failed to compile)\\.$"
34 | }
35 | },
36 | {
37 | "owner": "javascript",
38 | "severity": "error",
39 | "applyTo": "closedDocuments",
40 | "fileLocation": "absolute",
41 | "pattern": [
42 | {
43 | "regexp": "^(Module build failed:\\s.*?:\\s(.*):(.*?))[\\s+](?:\\(([\\d-]+):([\\d-]+)\\))?$",
44 | "message": 3,
45 | "file": 2,
46 | "line": 4,
47 | "column": 5
48 | }
49 | ]
50 | },
51 | {
52 | "owner": "javascript",
53 | "severity": "error",
54 | "applyTo": "closedDocuments",
55 | "fileLocation": "relative",
56 | "pattern": [
57 | {
58 | "regexp": "^ERROR in ./(.*)\\s?$",
59 | "file": 1
60 | },
61 | {
62 | "regexp": "^.*?Error:\\s(.*').*$",
63 | "message": 1
64 | },
65 | {
66 | "regexp": "^\\s+@.*?(\\d+)(?:-([\\d]+))?:(\\d+)(?:-([\\d]+))?\\s?$",
67 | "line": 1,
68 | "endLine": 2,
69 | "column": 3,
70 | "endColumn": 4
71 | }
72 | ]
73 | }
74 | ],
75 | "isBackground": true,
76 | "taskName": "Development"
77 | },
78 | {
79 | "args": [
80 | "run",
81 | "build",
82 | "--silent"
83 | ],
84 | "taskName": "Build Production"
85 | },
86 | {
87 | "args": [
88 | "run",
89 | "buildmochadebug",
90 | "--silent"
91 | ],
92 | "taskName": "BuildMochaDebug"
93 | },
94 | {
95 | "args": [
96 | "install",
97 | "--progress",
98 | "false",
99 | "--loglevel",
100 | "http"
101 | ],
102 | "taskName": "Install"
103 | },
104 | {
105 | "args": [
106 | "run",
107 | "builddll:dev",
108 | "--silent"
109 | ],
110 | "taskName": "Builddll Development"
111 | },
112 | {
113 | "args": [
114 | "publish"
115 | ],
116 | "taskName": "NPM Publish"
117 | },
118 | {
119 | "args": [
120 | "run",
121 | "lint",
122 | "--loglevel",
123 | "silent"
124 | ],
125 | "problemMatcher": [
126 | "$eslint-stylish"
127 | ],
128 | "taskName": "Lint"
129 | }
130 | ]
131 | }
132 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Written by Andrew Banks.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | This permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Redux library to simplify and reduce redux code.
2 |
3 | ### Advantages
4 | - Simple react style setState and getState functions for redux state management.
5 | - 100% compatibility with your current react-redux code base.
6 | - Automatic generation of mapStateToProps so that you do not have to write any.
7 | - Automatic generation of mapDispatchToProps so that you do not have to write any.
8 | - Automatic generation of the reducer so that you do not have to write any.
9 | - Reduced code size and code complexity as compared to react-redux.
10 | - Each reducer contains only one compare at each redux state transition which puts a small burden on performance.
11 | - Ability to handle a react component's life cycle events in a simple way in the model code separate from the react UI.
12 | - Complete separation of the UI from the business code according to a MVC standard.
13 | - Simple implementations for shared state management and shared business code without requiring additional reducers.
14 | - Thunk middleware is not required. Asynchronous UI calls are handled in the same way as synchronous UI calls.
15 |
16 | ### Installation
17 | `npm install simpler-redux`
18 |
19 | ### Migration from react-redux to simpler-redux
20 | 1) Install the simpler-redux libraty. `npm i simpler-redux`
21 | 2) Where you call the redux `createStore`, add the following where `reduxStore` is returned from the redux function `createStore`.
22 | ```javascript
23 | // It is assumed that this file is configureStore.js
24 | import { registerSimplerRedux } from 'simpler-redux'
25 |
26 | export const simplerReduxStore = registerSimplerRedux(reduxStore)
27 | ```
28 | 3) In your react-redux `Provider`, replace the redux store with the `simplerReduxStore` as shown below.
29 | ```javascript
30 | import React from 'react'
31 | import { Provider } from 'react-redux'
32 | import simplerReduxStore from './configureStore'
33 | import App from './App'
34 |
35 | export default () =>
36 |
37 |
38 |
39 | ```
40 | 4) Copy the simpler-redux MVC component scaffolding located [here](https://github.com/AndrewBanks10/simpler-redux-skeleton-project). It contains comments to guide your react component development.
41 |
42 | ### Basics
43 | #### _registerSimplerRedux_
44 | **Description**
45 | `registerSimplerRedux(reduxStore[, rootReducersObject[, preloadedState[, options]]])` - Wrap this around your redux `createStore` call. It returns an enhanced reduxStore object with the redux store as its prototype.
46 | **Parameters**
47 | `reduxStore` - The redux store that is returned by createStore.
48 | `rootReducersObject` - If you want to use dynamic reducer loading, specify this object parameter. Note that this is not the combineReducers object but the actual reducers object. If this is a new project, it is recommended that you use dynamic reducer loading exclusively. Then specifify this as the empty object {} and simpler-redux will automatically build your reducer and also load it when the component is constructed. Therefore, simpler-redux automatically supports react loadables without any code rewrites.
49 | `preloadedState`- If you specify the `rootReducersObject` and have state to preload then you must specify it in this parameter.
50 |
51 | **Return Value**
52 | A simpler redux store. Add this as a store prop to the react-redux Provider element.
53 |
54 | #### _createStore_
55 | **Description**
56 | `createStore([preloadedState[, enhancer]]])` - createStore calls redux createStore and supplies an interal reducer function, preloadedState and enhancers such as a redux logger. This solves the redux performance problem with calling every reducer on every state change. Only one reducer manages the entire redux state and does not require any case statements to merge state with the redux reducer key state(s). So, state changes only require one reducer call. However, this is incompatible with redux reducer code. Hence, only use this for simpler-redux only projects.
57 | **Parameters**
58 | `preloadedState`- redux preloadedState state.
59 | `enhancer` - redux enhancer.
60 |
61 | **Return Value**
62 | A simpler redux store. Add this as a store prop to the react-redux Provider element.
63 |
64 | #### _setRState_
65 | **Description**
66 | `simplerReduxStore.setRState(reducerKey, objectToMerge, [type])` - Merge the objectToMerge at the redux state object `reduxState[reducerKey]`.
67 | **Parameters**
68 | - `reducerKey` - Redux reducer key name. All state transitions will occur at `reduxState[reducerKey]`.
69 | - `objectToMerge` - This object will be merged at `reduxState[reducerKey]`.
70 | - `type` - Optional string name to identify the state change. This is the same thing as the action constant. It defaults to the `reducerKey`.
71 |
72 | **Return Value**
73 | None
74 |
75 | #### _getRState_
76 | **Description**
77 | `simplerReduxStore.getRState(reducerKey)` - Returns the state at `reduxState[reducerKey]`.
78 | **Parameters**
79 | `reducerKeyName` - Redux reducer key name.
80 | **Return Value**
81 | The state at `reduxState[reducerKey]`.
82 |
83 | #### _addListener_
84 | **Description**
85 | `simplerReduxStore.addListener(listener)` - Adds a listener that will be called immediately after any simpler-redux state change.
86 | **Parameters**
87 | `function(reducerKey, objMerged, type){}` - The function that will be called after the state change.
88 | **Return Value**
89 | Returns a function to remove the listener from simpler-redux.
90 |
91 | #### _generalReducer_
92 | **Description**
93 | `generalReducer(reducerKey, initialState)` - Returns a reducer function that manages the redux state transitions at `reduxState[reducerKey]`. Add this and the reducerKeyName to your root reducer object.
94 | **Parameters**
95 | - `reducerKey` - Redux reducer key name. All state transitions will occur at `reduxState[reducerKey]`.
96 | - `initialState` - This object will be merged at `reduxState[reducerKey]` for the initial state.
97 |
98 | **Return Value**
99 | A reducer function that manages the state transitions at `reducerKey`.
100 |
101 | #### _connectWithStore_
102 | **Description**
103 | Applies the redux connect function and returns a react HOC with the simpler-redux store in the props as `props.store`. This connects your UI component with your business code `selectors` and `serviceFunctions`.
104 | ```javascript
105 | connectWithStore({
106 | uiComponent,
107 | mapStateToProps,
108 | mapDispatchToProps,
109 | selectors,
110 | serviceFunctions,
111 | mergeProps,
112 | reduxOptions,
113 | storeIsDefinedCallback,
114 | noStoreParameterOnServiceFunctions,
115 | reducerKey,
116 | initialUIState,
117 | isDynamicReducer
118 | })
119 | ```
120 | **Object Parameters**
121 | - `uiComponent` - The react component that will be wrapped such that service functions and slices of state will be in the props.
122 | - `mapStateToProps` - You can write your own mapStateToProps but it is recommended that you use the selecters instead. Then a mapStateToProps will be automatically generated, based on the selectors object, and supplied to the redux connect function.
123 | - `mapDispatchToProps` - You can write your own mapDispatchToProps but it is recommended that you use the serviceFunctions instead. Then a mapDispatchToProps will be automatically generated, based on the serviceFunctions object, and supplied to the redux connect function.
124 | - `selectors` - The `selectors` is an object of functions that returns slices of the state at the reducerKey. It is used to build a mapStateToProps function for the redux connect. Below is an example.
125 | ```javascript
126 | // All keys in selectors will be in the props of the react component.
127 | // Each entry is a function that takes the entire redux state as a parameter
128 | // and then returns a slice of the state[reducerKey].
129 | export const selectors = {
130 | counter: state => state[reducerKey].counter
131 | }
132 | ```
133 | - `serviceFunctions` - The `serviceFunctions` is an object of functions that cause state changes at the reducerKey. It is used to build a mapDispatchToProps function for the redux connect.
134 | You can also handle business code here for react lifecycle events. So for code that is non-DOM oriented, the following keys are supported and the associated functions will be called at the particular react life cycle event.
135 | ```javascript
136 | onConstructor,
137 | onRender,
138 | componentDidMount,
139 | componentWillUnmount,
140 | componentDidCatch,
141 | componentToRender,
142 | ```
143 |
144 | Below is an example.
145 | ```javascript
146 | // All keys in serviceFunctions will be in the props of the react component.
147 | // Each entry is a function that takes the simpler-redux store as a parameter
148 | // and then performs state transitions by using store.setRState.
149 | // The functions may be synchronous or asynchronous and thunking middleware
150 | // is not required.
151 | export const serviceFunctions = {
152 | // Below is an example of an synchronous call.
153 | increment: store => store.setRState(reducerKey, { counter: store.getRState(reducerKey).counter + 1 }, 'increment'),
154 | // Below is an example of an asynchronous call.
155 | asyncCall: store => {
156 | store.setRState(reducerKey, { isBusy: true })
157 | someAsyncCall(
158 | data => store.setRState(reducerKey, { isBusy: false, data })
159 | )
160 | },
161 | // Below handles the componentDidMount react life cycle event.
162 | componentDidMount: store => handleLoadData(store)
163 | }
164 | ```
165 | - `mergeProps` - Redux connect `mergeProps` parameter.
166 | - `reduxOptions` - Redux connect `options` parameter.
167 | - `storeIsDefinedCallback` - Once the module's react component render is called for the first time, this callback will be invoked with the simpler redux store and the `stateAccessors` function as parameters. So it will be called before any possible user interactions.
168 | - `noStoreParameterOnServiceFunctions` - Used in conjunction with `storeIsDefinedCallback`. This will cause simpler-redux to not add the store parameter when calling the service functions. This should not be used for shared modules.
169 | - `reducerKey` - Used only in conjunction with `initialUIState`. When both of these are specified simpler-redux will build the mapStateToProps function directly from the keys in `initialUIState`.
170 | - `initialUIState` - Used only in conjunction with `reducerKey`. When both of these are specified simpler-redux will build the mapStateToProps function directly from the keys in `initialUIState`. This can be only used when your react component props only needs state from the declarative state definition `initialUIState`. Do not specify this and `selectors`. If this is not specified and `initialState` is then `initialUIState` will default to `initialState`.
171 | - `initialState` - Used only in conjunction with `reducerKey`. When both of these are specified simpler-redux will build the reducer that manages the state at `reducerKey`. If this is not specified and `initialUIState`is then `initialState` will default to `initialUIState`.
172 | - `isDynamicReducer` - Supports dynamic loading of the component and the reducer. If you specify the `rootReducersObject` parameter of your `registerSimplerRedux` call then simpler-redux automatically assumes isDynamicReducer is true. Set this to false if you want to manually load your reducer in the global reducers object for a specific react component.
173 | - `selectorList` - An array of {selectors, keylist[list of selector keys]}. This allows combining selectors from different modules into one in order to build a mapStateToProps that includes key/values from other reducers keys including the component reducer selectors. If you specify keylist then you can include only a subset of the selectors indtead of all of them.
174 | Below is an example.
175 | ```javascript
176 | import { selectors as counterSelectors } from './Counter'
177 |
178 | export const selectorList = [
179 | {counterSelectors, ['counter']}
180 | ]
181 | ```
182 | - `serviceFunctionList` - An array of {serviceFunctions, keylist[list of serviceFunctions keys], withStore}. This allows combining serviceFunctions from different modules into one in order to build a mapDispatchToProps that includes key/values from other module serviceFunctions. The keylist allows you to select only a subset of the associated service functions. The withStore set to true will cause the store to be the first parameter for all the service functions when called with the UI parameters following after.
183 | Below is an example.
184 | ```javascript
185 | import { serviceFunctions as counterServiceFunctions } from './Counter'
186 |
187 | export const serviceFunctionList = [
188 | {counterServiceFunctions, ['increment']}
189 | ]
190 | ```
191 |
192 | **Return Value**
193 | None
194 |
195 | #### _stateAccessors_
196 | **Description**
197 | `stateAccessors(store, reducerKey[, initialState])` - Returns a `setState`, `getState` and `reducerState` in an object that provides easier state management without having to provide a store or reducerKey. If you pass in initialState then it will also return reducerState which is a proxy to the reducerKey state. For getting state, reducerState.key is equivalent to getState().key. For setting state, reducerState.key = 1 is equivalent to setState({key: 1}). It is really just syntactic sugar for the getState and setState functions. The funciton stateAccessors should only be called as below in the storeIsDefinedCallback described above. Make sure to supply storeIsDefinedCallback to connectLifeCycleComponentWithStore or connectWithStore.
198 | ```javascript
199 | let setState, getState, reducerState
200 | export const storeIsDefinedCallback = (store, stateAccessors) =>
201 | ({setState, getState, reducerState} = stateAccessors(store, reducerKey, initialState))
202 |
203 | // Below are examples of using the functions.
204 | let counter = getState().counter
205 | setState({isBusy: true})
206 | reducerState.counter++
207 | counter = reducerState.counter
208 | reducerState.counter = 10
209 | ```
210 | #### _buildSelectorsFromUIState_
211 | **Description**
212 | `buildSelectorsFromUIState(reducerKey, uiInitialState) ` - Builds a selectors object from uiInitialState and returns that object.
213 | **Parameters**
214 | - `reducerKey` - Redux reducer key name.
215 | - `uiInitialState` - uiInitialState should only contain keys that you want in the props of the react component.
216 |
217 | **Return Value**
218 | A UI selectors object.
219 |
220 | ### Similarity to react's component state management
221 | - State transition
222 | - React - `this.setState(objToMerge)`. React does not guarantee that the state changes are applied immediately.
223 | - simpler-redux - `simplerReduxStore.setRState(reducerKey, objToMerge, [type])`. This is an immediate command.
224 | - Get state
225 | - React - `let x = this.state.x`
226 | - simpler-redux - `let x = simplerReduxStore.getRState(reducerKey)`
227 |
228 | ### Shared state management support
229 | With shared state management, you can write one module that performs state management and then any simpler-redux MVC module code can simply include the functionality into the initialState, selectors and serviceFunctions by using an import of the shared modules functions and writing just three lines of code.
230 | #### _makeSharedModuleKeyName_
231 | **Description**
232 | `makeSharedModuleKeyName(key, options)` - In order to avoid state and prop key collisions, this function returns the concatenation of the `key` followed by `options.id`.
233 | **Parameters**
234 | - `key` - The key that is used by the shared module as either a state key or service function key.
235 | - `options` - An object with a key suffix at the id key in the object. For example, `options.key ='AsyncModule'`.
236 | -
237 | **Return Value**
238 | The return value is the concatenation of the `key` followed by `options.id`.
239 |
240 | #### _createModuleData_
241 | **Description**
242 | `createModuleData(store, reducerKey[, initialState])` - Creates redux module data to replace typical module data. This way, you can manage and track changes to your module data through redux logging software.
243 |
244 | **Return Value**
245 | Returns a `setState`, `getState` and `reducerState` in an object.
246 |
247 | ### Sample usage of simpler-redux
248 | The model code is located at `src/Counter/model.js. This code manages the state for the reducerKey. It also is responsible for performing asynchronous operations and managing the state through those transactions. So, the model code contains the state management and the business logic.
249 | ```javascript
250 | export const reducerKey = 'counter.1'
251 | const counterKey = 'counter'
252 |
253 | const initialState = {
254 | [counterKey]: 0
255 | }
256 | // Selectors always take in the entire redux state object.
257 | // So, state[reducerKey] is the state object at the reducerKey.
258 | // simpler-redux constructs a mapStateToProps from this object and
259 | // calls redux connect using the constructed mapStateToProps.
260 | // The keys below will be in the props of the react component.
261 | export const selectors = {
262 | [counterKey]: state =>
263 | state[reducerKey][counterKey]
264 | }
265 | // serviceFunctions always takes the simpler-redux enhanced redux store as the first parameter.
266 | // Other parameters supplied by the react component come after.
267 | // simpler-redux constructs a mapDispatchToProps from this object and
268 | // calls redux connect using the constructed mapDispatchToProps.
269 | // The keys below will be in the props of the react conponent and
270 | // whenever the UI call the function in the props like say increment,
271 | // the increment function below will called with the store as a parameter
272 | // and the UI parameters follow after that.
273 | export const serviceFunctions = {
274 | increment: store =>
275 | store.setRState(reducerKey, { [counterKey]: store.getRState(reducerKey)[counterKey] + 1 }, 'increment')
276 | }
277 | ```
278 | The View code is located at src/Counter/view.js. It should only display information received in the props and call functions supplied by the props. This code is responsible for display only. Any functional behavior should be supplied by the model to the controller and then into the props of the view where is is simply called by the view.
279 | ```javascript
280 | import React from 'react'
281 |
282 | export default ({counter, increment}) =>
283 |
284 |
Counter: {counter}
285 |
286 |
287 | ```
288 | The controller code is located at `src/Counter/index.js`. This is generally the same for all controllers so it is basically a copy and paste.
289 | ```javascript
290 | import { connectWithStore, generalReducer } from 'simpler-redux'
291 | import uiComponent from './view/view'
292 | import * as modelDefinition from './model/model'
293 |
294 | // Use connectLifeCycleComponentWithStore to handle react life cycle events in the serviceFunctions object in the model code.
295 | export default connectWithStore({ uiComponent, ...modelDefinition })
296 | export const reducerKey = modelDefinition.reducerKey
297 | export const reducer = generalReducer(reducerKey, modelDefinition.initialState)
298 | // Simpler-redux builds the reducer for you. Add reducerKey and reducer to your global reducer object.
299 | ```
300 | Note that the above controller code does not contain any intelligence. Its purpose is simply to connect the model code to the view code without knowing anything about the details of either's implementions. Therefore, this technique removes any side effects of changing the model code or underlying state. Also, this code can be copy and pasted for any component because it does the same thing for most simpler-redux component modules.
301 | The src/Counter/index.js code exports features of the simpler-redux MVC module to the outside.
302 |
303 | The reducer code located at `src/reducers.js`.
304 | ```javascript
305 | import { reducerKey as counterReducerKey, reducer as counterReducer } from './Counter'
306 |
307 | export default {
308 | [counterReducerKey]: counterReducer
309 | }
310 | ```
311 | The redux store code is located at `src/reduxstore.js`.
312 | ```javascript
313 | import reducersObject from './reducers'
314 | import { createStore, combineReducers } from 'redux'
315 | import { registerSimplerRedux } from 'simpler-redux'
316 |
317 | export default registerSimplerRedux(createStore(combineReducers(reducersObject)))
318 | ```
319 | Last, below is the App code located at `src/App.jsx`.
320 | ```javascript
321 | import React from 'react'
322 | import { Provider } from 'react-redux'
323 | import store from './reduxstore'
324 | import Counter from './Counter'
325 |
326 | export default () =>
327 |
328 |
329 |
330 | ```
331 | ### Sample usage of simpler-redux shared state management code
332 | ##### Shared module
333 | Below is the style of code that would be written for shared state management. The state keys and service function keys all will have an input module id appended to the end of the keys. This prevents key collisions in the props of react components and keys in the reducer key state.
334 | ```javascript
335 | import { makeSharedModuleKeyName } from 'simpler-redux'
336 | const counterKey = 'counter'
337 | const incrementKey = 'increment'
338 |
339 | const makeCounterKey = options =>
340 | makeSharedModuleKeyName(counterKey, options)
341 |
342 | // The key below is 'counter' + options.id. This way the consumer of the
343 | // shared module can make the keys what they want to avoid key collisions.
344 | export const getInitialState = options => ({
345 | [makeCounterKey(options)]: 0
346 | })
347 |
348 | // The key below is 'counter' + options.id
349 | export const getSelectors = (reducerKey, options) => {
350 | return {
351 | [makeCounterKey(options)]: state => state[reducerKey][makeCounterKey(options)]
352 | }
353 | }
354 |
355 | // All shared module serviceFunction functions have the following signature.
356 | export const getServiceFunctions = (reducerKey, options) => {
357 | return {
358 | [makeSharedModuleKeyName(incrementKey, options)]: store => {
359 | // Change the state at 'counter' + options.id
360 | store.setRState(
361 | reducerKey,
362 | { [makeCounterKey(options)]: store.getRState(reducerKey)[makeCounterKey(options)] + 1 }
363 | )
364 | }
365 | }
366 | }
367 | ```
368 | ##### Consumer module
369 | Next is the code that consumes the above shared module.
370 | ```javascript
371 | import { generalReducer } from 'simpler-redux'
372 | import {
373 | getServiceFunctions as sharedCounterGetServiceFunctions,
374 | getSelectors as sharedCounterGetSelectors,
375 | getInitialState as sharedCounterInitialState
376 | } from '../SharedModel/Counter'
377 |
378 | export const reducerKey = 'counter.1'
379 | const asyncModuleId = 'CounterModule'
380 | const baseOptions = { id: asyncModuleId }
381 |
382 | const initialState = {
383 | ...sharedCounterInitialState(baseOptions)
384 | }
385 |
386 | export const selectors = {
387 | ...sharedCounterGetSelectors(reducerKey, baseOptions)
388 | }
389 |
390 | export const serviceFunctions = {
391 | ...sharedCounterGetServiceFunctions(reducerKey, baseOptions)
392 | }
393 |
394 | export const reducer = generalReducer(reducerKey, initialState)
395 | ```
396 | Here are the advantages of the above implementation.
397 | 1. No additional reducers are required which would degrade performance.
398 | 2. The consumer does not participate in the state management.
399 | 3. Your organization can collect this sharable state management code into libraries so that you do not have to repeat yourself in various projects.
400 | 4. The sharable code and the consumer code share the same paradigm (initialState, selectors, serviceFunctions). Therefore, the code is easy to understand.
401 | 5. This technique is simpler and easier to maintain as compared to the shared state management of redux.
402 |
403 | So with simpler-redux state management you only need to think about three simple concepts:
404 | 1. initialState to initialize the state at the reducer key.
405 | 2. selectors to get slices of the current state at the reducer key.
406 | 3. serviceFunctions to state transition slices of the current state at the reducer key or call asynchronous functions.
407 |
408 | Note that simpler-redux also converts react-redux into an MVC implementation for the react UI. As most will know, MVC is the correct way to implement user interface code. In the simpler-redux case, MVC is as follows.
409 | 1. The view is the stateless react component.
410 | 2. The model is the state management and service function code.
411 | 3. The controller is responsible for providing the connections between the view and model.
412 |
--------------------------------------------------------------------------------
/devtools/devServer.js:
--------------------------------------------------------------------------------
1 | /*eslint no-console: 0*/
2 | const webpack = require('webpack');
3 | const WebpackDevServer = require('webpack-dev-server');
4 | const config = require('./webpack.config.dev');
5 |
6 | const port = process.env.npm_package_config_port || 3000;
7 | const host = process.env.npm_package_config_host || 'localhost';
8 |
9 | new WebpackDevServer(webpack(config), {
10 | publicPath: config.output.publicPath,
11 | hot: true,
12 | historyApiFallback: true,
13 | stats: {
14 | colors: true,
15 | chunks: false,
16 | 'errors-only': true
17 | }
18 | }).listen(port, host, function (err) {
19 | if (err) {
20 | console.log(err);
21 | }
22 |
23 | console.log(`Listening at http://${host}:${port}/`);
24 | });
--------------------------------------------------------------------------------
/devtools/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | Simpler Redux
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/devtools/webpack.config.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const libraryName = 'simpler-redux'
4 |
5 | // Entry point for your library. This is assumed to be in the src directory at the root level.
6 | const libEntryJs = 'index.js'
7 |
8 | // Entry point for your minimized build. This is assumed to be in the src directory at the root level.
9 | const buildEntryJs = 'index.js'
10 |
11 | // List of modules you require for your library and do not want them contained in your own
12 | // library module. For example, you would not want react code contained in your library so that
13 | // would be in the list below. But you may want an Object.assign polyfill in your library. So,
14 | // do not list object-assign and that will be included in your library.
15 | // The key item is the library name and the second is how you will refer to it in
16 | // your code.
17 | const externalsLibMin = {
18 | 'redux': 'Redux',
19 | 'react': 'React'
20 | }
21 |
22 | const externalsLibLib = {
23 | 'redux': 'redux',
24 | 'react': 'react',
25 | 'react-redux': 'react-redux',
26 | 'prop-types': 'prop-types'
27 | }
28 |
29 | /*
30 | This is a list of modules used to create a dll library.
31 | Important for faster compiling since these rarely change.
32 | Note: If you add or subtract from this list or update any modules in the list, pull up the task list
33 | and select "Builddll Development". Otherwise, run the command npm run builddll:dev.
34 | */
35 | const dllModules = [
36 | 'redux',
37 | 'react',
38 | 'react-dom',
39 | 'react-redux',
40 | 'object-assign'
41 | ]
42 |
43 | const config = {
44 | sourceDir: 'src', // Source directory.
45 | buildDir: 'dist', // Build directory
46 | libDir: 'lib', // Library build directory
47 | libEntryJs,
48 | buildEntryJs,
49 | libraryName: libraryName,
50 | bundleName: 'bundle', // For development debug only.
51 | testEntryJs: 'test.dev/index.dev.js', // For development debug only.
52 | htmlTemplate: 'index.ejs', // For development debug only.
53 |
54 | // List of modules you require for your library and do not want them contained in your own
55 | // library module. For example, you would not want react code contained in your library so that
56 | // would be in the list below. But you may want an Object.assign polyfill in your library.
57 | // The key item is the library name and the second is how you will refer to it in
58 | // your code.
59 | externalsLibMin,
60 | externalsLibLib,
61 |
62 | dllDir: 'devdll', // Build directory for the the development dll library.
63 | dllBundleName: 'dllbundle',
64 | dllModules,
65 | htmlDevTemplate: 'index.dev.ejs', // Used for the development html template. This is built.
66 |
67 | libraryTarget: 'commonjs2',
68 | module: {
69 | loaders: [
70 | {
71 | test: /\.js$/,
72 | exclude: /node_modules/,
73 | loader: 'babel-loader'
74 | }
75 | ]
76 | },
77 | node_modulesPath: 'node_modules',
78 | resolveExtensions: ['.js', '.jsx']
79 | }
80 |
81 | config.basePath = process.cwd()
82 |
83 | // At this time, the source directory must be directly below the vscode directory.
84 | config.absoluteSourcePath = path.join(config.basePath, config.sourceDir)
85 |
86 | // At this time, the build directory must be directly below the vscode directory.
87 | config.absoluteBuildPath = path.join(config.basePath, config.buildDir)
88 |
89 | // At this time, the lib directory must be directly below the vscode directory.
90 | config.absoluteLibPath = path.join(config.basePath, config.libDir)
91 |
92 | // At this time, the dll directory must be directly below the vscode directory.
93 | config.absoluteDllPath = path.join(config.basePath, config.dllDir)
94 |
95 | config.absoluteDevToolsPath = path.join(config.basePath, 'devtools')
96 |
97 | config.resolveEntry = {
98 | modules: [
99 | config.absoluteSourcePath,
100 | config.node_modulesPath
101 | ],
102 | extensions: config.resolveExtensions
103 | }
104 |
105 | module.exports = config
106 |
--------------------------------------------------------------------------------
/devtools/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const HtmlWebpackPlugin = require('html-webpack-plugin')
4 | const config = require('./webpack.config.config.js')
5 |
6 | const devConfig = {
7 | devtool: 'cheap-module-source-map',
8 | watch: true,
9 | entry: [
10 | `webpack-dev-server/client?http://${process.env.npm_package_config_host}:${process.env.npm_package_config_port}`,
11 | path.join(config.basePath, config.testEntryJs)
12 | ],
13 | output: {
14 | path: config.absoluteBuildPath,
15 | filename: `${config.bundleName}.js`
16 | },
17 | plugins: [
18 | new webpack.DefinePlugin({
19 | '__DEV__': true,
20 | 'process.env': {
21 | 'NODE_ENV': JSON.stringify('development')
22 | }
23 | }),
24 | new webpack.HotModuleReplacementPlugin(),
25 | new webpack.NamedModulesPlugin()
26 | ],
27 | module: config.module,
28 | resolve: config.resolveEntry
29 | }
30 |
31 | if (config.dllModules.length > 0) {
32 | devConfig.plugins.push(
33 | new webpack.DllReferencePlugin({
34 | context: config.absoluteDllPath,
35 | manifest: require(path.join(config.absoluteDllPath, `${config.dllBundleName}.json`))
36 | })
37 | )
38 | devConfig.plugins.push(
39 | new HtmlWebpackPlugin({
40 | template: config.htmlDevTemplate,
41 | inject: 'body'
42 | })
43 | )
44 | } else {
45 | devConfig.plugins.push(
46 | new HtmlWebpackPlugin({
47 | template: path.join(config.absoluteDevToolsPath, config.htmlTemplate),
48 | inject: 'body'
49 | })
50 | )
51 | }
52 |
53 | module.exports = devConfig
54 |
--------------------------------------------------------------------------------
/devtools/webpack.config.dist.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const config = require('./webpack.config.config')
3 |
4 | module.exports = {
5 | entry: path.join(config.absoluteSourcePath, config.buildEntryJs),
6 | output: {
7 | path: config.absoluteBuildPath,
8 | filename: `${config.libraryName}.js`
9 | },
10 | externals: config.externalsLibMin,
11 | module: config.module
12 | }
13 |
--------------------------------------------------------------------------------
/devtools/webpack.config.dist.min.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const config = require('./webpack.config.config');
3 | const webpack = require('webpack');
4 |
5 | module.exports = {
6 | entry: path.join(config.absoluteBuildPath, `${config.libraryName}.js`),
7 | output: {
8 | path: config.absoluteBuildPath,
9 | filename: `${config.libraryName}.min.js`,
10 | },
11 | plugins: [
12 | new webpack.DefinePlugin({
13 | '__DEV__': false,
14 | 'process.env': {
15 | 'NODE_ENV': JSON.stringify('production')
16 | }
17 | }),
18 | new webpack.optimize.UglifyJsPlugin({
19 | compressor: {
20 | warnings: false
21 | },
22 | output: {
23 | comments: false
24 | }
25 | })
26 | ],
27 | };
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/devtools/webpack.config.dlllib.dev.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const config = require('./webpack.config.config')
4 | const HtmlWebpackPlugin = require('html-webpack-plugin')
5 |
6 | module.exports = {
7 | entry: {
8 | dllModules: config.dllModules
9 | },
10 |
11 | output: {
12 | filename: `${config.dllBundleName}.js`,
13 | path: config.absoluteDllPath,
14 | library: config.dllBundleName
15 | },
16 |
17 | plugins: [
18 | new webpack.DllPlugin({
19 | context: config.absoluteDllPath,
20 | name: config.dllBundleName,
21 | path: path.join(config.absoluteDllPath, `${config.dllBundleName}.json`)
22 | }),
23 | new HtmlWebpackPlugin({
24 | template: path.join(config.absoluteDevToolsPath, config.htmlTemplate),
25 | filename: path.join(config.basePath, config.htmlDevTemplate),
26 | inject: 'body',
27 | hash: true
28 | })
29 | ],
30 | resolve: config.resolveEntry
31 | }
32 |
--------------------------------------------------------------------------------
/devtools/webpack.config.lib.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const config = require('./webpack.config.config')
3 |
4 | module.exports = {
5 | entry: path.join(config.absoluteSourcePath, config.libEntryJs),
6 | output: {
7 | path: config.absoluteLibPath,
8 | filename: `${config.libraryName}.js`,
9 | libraryTarget: config.libraryTarget
10 | },
11 | externals: config.externalsLibLib,
12 | module: config.module
13 | }
14 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true
4 | },
5 | "exclude": [
6 | "typings/main.d.ts",
7 | "typings/main",
8 | "node_modules"
9 | ]
10 | }
--------------------------------------------------------------------------------
/lib/simpler-redux.js:
--------------------------------------------------------------------------------
1 | module.exports =
2 | /******/ (function(modules) { // webpackBootstrap
3 | /******/ // The module cache
4 | /******/ var installedModules = {};
5 | /******/
6 | /******/ // The require function
7 | /******/ function __webpack_require__(moduleId) {
8 | /******/
9 | /******/ // Check if module is in cache
10 | /******/ if(installedModules[moduleId]) {
11 | /******/ return installedModules[moduleId].exports;
12 | /******/ }
13 | /******/ // Create a new module (and put it into the cache)
14 | /******/ var module = installedModules[moduleId] = {
15 | /******/ i: moduleId,
16 | /******/ l: false,
17 | /******/ exports: {}
18 | /******/ };
19 | /******/
20 | /******/ // Execute the module function
21 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22 | /******/
23 | /******/ // Flag the module as loaded
24 | /******/ module.l = true;
25 | /******/
26 | /******/ // Return the exports of the module
27 | /******/ return module.exports;
28 | /******/ }
29 | /******/
30 | /******/
31 | /******/ // expose the modules object (__webpack_modules__)
32 | /******/ __webpack_require__.m = modules;
33 | /******/
34 | /******/ // expose the module cache
35 | /******/ __webpack_require__.c = installedModules;
36 | /******/
37 | /******/ // define getter function for harmony exports
38 | /******/ __webpack_require__.d = function(exports, name, getter) {
39 | /******/ if(!__webpack_require__.o(exports, name)) {
40 | /******/ Object.defineProperty(exports, name, {
41 | /******/ configurable: false,
42 | /******/ enumerable: true,
43 | /******/ get: getter
44 | /******/ });
45 | /******/ }
46 | /******/ };
47 | /******/
48 | /******/ // getDefaultExport function for compatibility with non-harmony modules
49 | /******/ __webpack_require__.n = function(module) {
50 | /******/ var getter = module && module.__esModule ?
51 | /******/ function getDefault() { return module['default']; } :
52 | /******/ function getModuleExports() { return module; };
53 | /******/ __webpack_require__.d(getter, 'a', getter);
54 | /******/ return getter;
55 | /******/ };
56 | /******/
57 | /******/ // Object.prototype.hasOwnProperty.call
58 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
59 | /******/
60 | /******/ // __webpack_public_path__
61 | /******/ __webpack_require__.p = "";
62 | /******/
63 | /******/ // Load entry module and return exports
64 | /******/ return __webpack_require__(__webpack_require__.s = 6);
65 | /******/ })
66 | /************************************************************************/
67 | /******/ ([
68 | /* 0 */
69 | /***/ (function(module, exports, __webpack_require__) {
70 |
71 | "use strict";
72 |
73 |
74 | if (true) {
75 | module.exports = __webpack_require__(8);
76 | } else {
77 | module.exports = require('./dist/react-hot-loader.development.js');
78 | }
79 |
80 |
81 | /***/ }),
82 | /* 1 */
83 | /***/ (function(module, exports) {
84 |
85 | module.exports = function(module) {
86 | if(!module.webpackPolyfill) {
87 | module.deprecate = function() {};
88 | module.paths = [];
89 | // module.parent = undefined by default
90 | if(!module.children) module.children = [];
91 | Object.defineProperty(module, "loaded", {
92 | enumerable: true,
93 | get: function() {
94 | return module.l;
95 | }
96 | });
97 | Object.defineProperty(module, "id", {
98 | enumerable: true,
99 | get: function() {
100 | return module.i;
101 | }
102 | });
103 | module.webpackPolyfill = 1;
104 | }
105 | return module;
106 | };
107 |
108 |
109 | /***/ }),
110 | /* 2 */
111 | /***/ (function(module, exports, __webpack_require__) {
112 |
113 | "use strict";
114 | /* WEBPACK VAR INJECTION */(function(module, process) {Object.defineProperty(exports, "__esModule", { value: true });exports.createModuleData = exports.stateAccessors = exports.reducersPreloadedState = exports.makeSharedModuleKeyName = exports.setStateFunction = exports.getStateFunction = exports.createStore = exports.registerSimplerRedux = exports.generalReducer = exports.srOptions = undefined;var _extends = Object.assign || function (target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {target[key] = source[key];}}}return target;};
115 |
116 |
117 | var _redux = __webpack_require__(7);
118 | var _util = __webpack_require__(4);
119 | var _proxy = __webpack_require__(9);var _proxy2 = _interopRequireDefault(_proxy);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}(function () {var enterModule = __webpack_require__(0).enterModule;enterModule && enterModule(module);})();function _defineProperty(obj, key, value) {if (key in obj) {Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });} else {obj[key] = value;}return obj;} /*
120 | Written by Andrew Banks. MIT license.
121 | */var simplerReduxReducerKey = '@@@@@srReducerKey';
122 | var simplerReduxObjectToMergeKey = '@@@@@srObjectToMergeKey';
123 |
124 | var objectType = function objectType(obj) {return Object.prototype.toString.call(obj).slice(8, -1);};
125 | var isObjectType = function isObjectType(obj) {return objectType(obj) === 'Object';};
126 |
127 | var srOptions = exports.srOptions = void 0;
128 |
129 | var makeSetRState = function makeSetRState(reduxStore) {
130 | return function (reducerKey, objToMerge, type) {var _reduxStore$dispatch;
131 | if (type === undefined) {
132 | type = reducerKey;
133 | }
134 | if (process.env.NODE_ENV !== 'production') {
135 | if (typeof reducerKey !== 'string') {
136 | throw new Error('setRState: The first argument must be a string.');
137 | }
138 | if (!isObjectType(objToMerge)) {
139 | throw new Error('setRState: The second argument must be a primitive object type.');
140 | }
141 | if (typeof type !== 'string') {
142 | throw new Error('setRState: The third argument must be a string.');
143 | }
144 | }
145 |
146 | reduxStore.dispatch((_reduxStore$dispatch = {}, _defineProperty(_reduxStore$dispatch,
147 | simplerReduxReducerKey, reducerKey), _defineProperty(_reduxStore$dispatch,
148 | simplerReduxObjectToMergeKey, objToMerge), _defineProperty(_reduxStore$dispatch, 'type',
149 | type), _reduxStore$dispatch));
150 |
151 |
152 | reduxStore.listeners.forEach(function (listenerObj) {
153 | listenerObj.listener(reducerKey, objToMerge, type);
154 | });
155 | };
156 | };
157 |
158 | var makeGetRState = function makeGetRState(reduxStore) {
159 | return function (reducerKey) {
160 | var state = reduxStore.getState()[reducerKey];
161 | if (process.env.NODE_ENV !== 'production') {
162 | if (typeof reducerKey !== 'string') {
163 | throw new Error('getRState: The first argument must be a string.');
164 | }
165 | if (state === undefined) {
166 | throw new Error('The reducerKey state at ' + reducerKey + ' is undefined. Did you forget to export an initialUIState or initialState from your model code?');
167 | }
168 | }
169 | return state;
170 | };
171 | };
172 |
173 | // These listeners do what redux subscribers should have done. It gives the reducerKey being modified
174 | // so that a listener can quickly decide if it is concerned about changes in that reducerKey.
175 | var addListener = function addListener(store) {
176 | return function (listener) {
177 | if (process.env.NODE_ENV !== 'production') {
178 | if (typeof listener !== 'function') {
179 | throw new Error('addListener: The first argument must be a function.');
180 | }
181 | }
182 | var id = store.listenerId++;
183 | store.listeners.push({ listener: listener, id: id });
184 | // Return a function that will remove this listener.
185 | return function () {
186 | var i = 0;
187 | for (; i < store.listeners.length && store.listeners[i].id !== id; ++i) {}
188 | if (i < store.listeners.length) {
189 | store.listeners.splice(i, 1);
190 | }
191 | };
192 | };
193 | };
194 |
195 | //
196 | // Call this to generate your reducer.
197 | //
198 | var generalReducer = exports.generalReducer = function generalReducer(reducerKey, initialState) {
199 | if (process.env.NODE_ENV !== 'production') {
200 | if (reducerKey === undefined) {
201 | throw new Error('generalReducer: reducerKey must be defined.');
202 | }
203 | if (initialState === undefined) {
204 | throw new Error('generalReducer: initialState must be defined.');
205 | }
206 | }
207 | initialState = _extends({}, initialState);
208 | return function () {var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : initialState;var action = arguments[1];
209 | if (action[simplerReduxReducerKey] === reducerKey) {
210 | return _extends({}, state, action[simplerReduxObjectToMergeKey]);
211 | }
212 | return state;
213 | };
214 | };
215 |
216 | // Allows dynamic loading of simpler redux mvc components and their associated reducers.
217 | var buildAddReducer = function buildAddReducer(store, preloadedState) {
218 | return function (reducerKey, initialState) {
219 | if (process.env.NODE_ENV !== 'production') {
220 | if (reducerKey === undefined) {
221 | throw new Error('addReducer: The first argument (reducerKey) must be defined.');
222 | }
223 | if (initialState === undefined) {
224 | throw new Error('addReducer: The second argument (initialState) must be defined.');
225 | }
226 | }
227 | // First load the initial state for the reducer.
228 | var state = _extends({}, initialState);
229 | // Then load the preloadedState for the keys that exist in the initial state.
230 | // Therefore, keys in preloadedState[reducerKey] that are not part of the current
231 | // state shape in initialState are considered deprecated and are ignored.
232 | var preloadedStateAtReducerKey = preloadedState[reducerKey];
233 | if (preloadedStateAtReducerKey !== undefined) {
234 | Object.keys(preloadedStateAtReducerKey).forEach(function (key) {
235 | if (state[key] !== undefined) {
236 | state[key] = preloadedStateAtReducerKey[key];
237 | }
238 | });
239 | }
240 | // One reducer with no typical redux reducers.
241 | if (store.isOneReducer) {
242 | var currentState = store.getState();
243 | // Do not use initialState on HMR.
244 | if (currentState === undefined || currentState[reducerKey] === undefined) {
245 | // Set the initialState state at the reducerKey.
246 | store.setRState(reducerKey, state);
247 | }
248 | return;
249 | }
250 | // Generate the reducer for reducerKey.
251 | var reducer = generalReducer(reducerKey, state);
252 | // Add the reducer to the current list.
253 | store.currentReducersObject = _extends({}, store.currentReducersObject, _defineProperty({}, reducerKey, reducer));
254 | // Replace the redux reducers with the new list.
255 | store.replaceReducer((0, _redux.combineReducers)(store.currentReducersObject));
256 | };
257 | };
258 |
259 | //
260 | // This must be called with the redux store as a parameter after a createStore.
261 | // Then use the return of this function in the react-redux Provider element as the store.
262 | // If you pass in a rootReducersObject then you can use simpler-redux dynamic loading of reducers.
263 | // Note: rootReducersObject is the actual reducers object and not the combineReducers output.
264 | // If you want only dynamic reducers, use state => state (null reducer) for the redux createStore reducer
265 | // and { } as the rootReducersObject for the call below.
266 | // If you use rootReducersObject then you should also pass in preloadedState (if it exists).
267 | // options
268 | // 1) isDynamicReducer - Default to dynamic reducer loading for react components so that you do not have to specify isDynamicReducer in each component module.
269 | //
270 | var registerSimplerRedux = exports.registerSimplerRedux = function registerSimplerRedux(
271 | reduxStore,
272 | rootReducersObject)
273 |
274 |
275 | {var preloadedState = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
276 | exports.srOptions = srOptions = (0, _util.defaultOptions)(options);
277 | var wrappedReduxStore = Object.create(reduxStore);
278 | wrappedReduxStore.setRState = makeSetRState(wrappedReduxStore);
279 | wrappedReduxStore.getRState = makeGetRState(wrappedReduxStore);
280 | wrappedReduxStore.addListener = addListener(wrappedReduxStore);
281 | wrappedReduxStore.listenerId = 0;
282 | wrappedReduxStore.listeners = [];
283 | // Support for dynamic reducer loading.
284 | if (rootReducersObject !== undefined) {
285 | wrappedReduxStore.isDynamicReducerLoading = function () {return true;};
286 | wrappedReduxStore.currentReducersObject = _extends({}, rootReducersObject);
287 | wrappedReduxStore.addReducer = buildAddReducer(wrappedReduxStore, _extends({}, preloadedState));
288 | } else {
289 | wrappedReduxStore.isDynamicReducerLoading = function () {return false;};
290 | if (process.env.NODE_ENV !== 'production') {
291 | wrappedReduxStore.addReducer = function () {
292 | throw new Error('To call addReducer, you must specify a rootReducersObject in the 2nd argument of registerSimplerRedux which can be just {}.');
293 | };
294 | }
295 | }
296 | return wrappedReduxStore;
297 | };
298 |
299 | //
300 | // One reducer is not compatible with existing redux code. Must be all simpler-redux.
301 | // This is the only reducer called for all state transitions.
302 | //
303 | var oneReducer = function oneReducer() {var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};var action = arguments[1];
304 | var reducerKey = action[simplerReduxReducerKey];
305 | var objToMerge = action[simplerReduxObjectToMergeKey];
306 | // This is some redux thing, not from our setRState.
307 | if (reducerKey === undefined) {
308 | return state;
309 | }
310 | // Must change the upper level redux state pointer or redux does not recognize a state change.
311 | state = _extends({}, state);
312 | // Merge the incoming reducerKey state at the reducerKey
313 | state[reducerKey] = _extends({}, state[reducerKey], objToMerge);
314 | return state;
315 | };
316 |
317 | // This cannot be used with redux reducers.
318 | var createStore = exports.createStore = function createStore(preloadedState, enhancer) {var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
319 | var reduxStore = (0, _redux.createStore)(
320 | oneReducer,
321 | preloadedState,
322 | enhancer);
323 |
324 | var wrappedReduxStore = registerSimplerRedux(
325 | reduxStore,
326 | {},
327 | preloadedState,
328 | options);
329 |
330 | wrappedReduxStore.isOneReducer = true;
331 | return wrappedReduxStore;
332 | };
333 |
334 | // This makes it easier to access reducerKey state previously given a reducerKey.
335 | var getStateFunction = exports.getStateFunction = function getStateFunction(reducerKey) {return (
336 | function (state, key) {return (
337 | state[reducerKey][key]);});};
338 |
339 | // This makes it easier to set reducerKey state previously given a reducerKey.
340 | var setStateFunction = exports.setStateFunction = function setStateFunction(reducerKey) {return (
341 | function (store, mergeState, type) {return (
342 | store.setRState(reducerKey, mergeState, type));});};
343 |
344 | // Use this to generate shared module keys.
345 | var makeSharedModuleKeyName = exports.makeSharedModuleKeyName = function makeSharedModuleKeyName(key, options) {return '' +
346 | key + options.id;};
347 |
348 | // Sifts out dynamically loaded reducer keys from the preloaded state in order to avoid
349 | // a redux warning. Use the return of this function to pass into the redux createStore.
350 | var reducersPreloadedState = exports.reducersPreloadedState = function reducersPreloadedState(reducersObject, preloadedState) {return (
351 | Object.keys(preloadedState).reduce(function (obj, key) {
352 | if (reducersObject[key] !== undefined) {
353 | obj[key] = preloadedState[key];
354 | }
355 | return obj;
356 | }, {}));};
357 |
358 | var getState = function getState(store, reducerKey) {return (
359 | function () {return store.getRState(reducerKey);});};
360 |
361 | var setState = function setState(store, reducerKey) {return (
362 | function (mergeState, type) {
363 | if (process.env.NODE_ENV !== 'production') {
364 | if (!isObjectType(mergeState)) {
365 | throw new Error('setState: The first argument must be a primitive object type.');
366 | }
367 | }
368 | store.setRState(reducerKey, mergeState, type);
369 | });};
370 |
371 | //
372 | // Only call this in the storeIsDefinedCallback sent into connectWithStore above.
373 | // Use the store parameter provided in connectWithStore along with the reducerKey
374 | // in the module.
375 | //
376 | var stateAccessors = exports.stateAccessors = function stateAccessors(store, reducerKey, initialState) {
377 | if (process.env.NODE_ENV !== 'production') {
378 | if (store === undefined) {
379 | throw new Error('The first parameter (store) to stateAccessors must be defined.');
380 | }
381 | if (typeof reducerKey !== 'string') {
382 | throw new Error('The second parameter (reducerKey) to stateAccessors must be a string.');
383 | }
384 | }
385 | var ret = {
386 | getState: getState(store, reducerKey),
387 | setState: setState(store, reducerKey) };
388 |
389 |
390 | if (initialState !== undefined) {
391 | ret.reducerState = (0, _proxy2.default)(store, reducerKey, initialState);
392 | }
393 |
394 | return ret;
395 | };
396 |
397 | // Creates general purpose module data under a redux reducerKey.
398 | // A redux store must be imported from your redux createStore module for the argument below.
399 | var createModuleData = exports.createModuleData = function createModuleData(store, reducerKey, initialState) {
400 | if (process.env.NODE_ENV !== 'production') {
401 | if (!store.isDynamicReducerLoading()) {
402 | throw new Error('To call createModuleData, you must specify a rootReducersObject in the 2nd argument of registerSimplerRedux which can be just {}.');
403 | }
404 | if (store === undefined) {
405 | throw new Error('The first parameter (store) to createModuleData must be defined.');
406 | }
407 | if (typeof reducerKey !== 'string') {
408 | throw new Error('The seccond parameter (reducerKey) to createModuleData must a string.');
409 | }
410 | if (initialState === undefined) {
411 | throw new Error('The third parameter (initialState) to createModuleData must be defined.');
412 | }
413 | }
414 | store.addReducer(reducerKey, initialState);
415 | return stateAccessors(store, reducerKey, initialState);
416 | };;(function () {var reactHotLoader = __webpack_require__(0).default;var leaveModule = __webpack_require__(0).leaveModule;if (!reactHotLoader) {return;}reactHotLoader.register(simplerReduxReducerKey, 'simplerReduxReducerKey', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(simplerReduxObjectToMergeKey, 'simplerReduxObjectToMergeKey', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(objectType, 'objectType', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(isObjectType, 'isObjectType', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(srOptions, 'srOptions', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(makeSetRState, 'makeSetRState', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(makeGetRState, 'makeGetRState', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(addListener, 'addListener', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(generalReducer, 'generalReducer', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(buildAddReducer, 'buildAddReducer', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(registerSimplerRedux, 'registerSimplerRedux', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(oneReducer, 'oneReducer', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(createStore, 'createStore', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(getStateFunction, 'getStateFunction', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(setStateFunction, 'setStateFunction', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(makeSharedModuleKeyName, 'makeSharedModuleKeyName', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(reducersPreloadedState, 'reducersPreloadedState', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(getState, 'getState', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(setState, 'setState', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(stateAccessors, 'stateAccessors', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');reactHotLoader.register(createModuleData, 'createModuleData', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/simpler-redux.js');leaveModule(module);})();;
417 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)(module), __webpack_require__(3)))
418 |
419 | /***/ }),
420 | /* 3 */
421 | /***/ (function(module, exports) {
422 |
423 | // shim for using process in browser
424 | var process = module.exports = {};
425 |
426 | // cached from whatever global is present so that test runners that stub it
427 | // don't break things. But we need to wrap it in a try catch in case it is
428 | // wrapped in strict mode code which doesn't define any globals. It's inside a
429 | // function because try/catches deoptimize in certain engines.
430 |
431 | var cachedSetTimeout;
432 | var cachedClearTimeout;
433 |
434 | function defaultSetTimout() {
435 | throw new Error('setTimeout has not been defined');
436 | }
437 | function defaultClearTimeout () {
438 | throw new Error('clearTimeout has not been defined');
439 | }
440 | (function () {
441 | try {
442 | if (typeof setTimeout === 'function') {
443 | cachedSetTimeout = setTimeout;
444 | } else {
445 | cachedSetTimeout = defaultSetTimout;
446 | }
447 | } catch (e) {
448 | cachedSetTimeout = defaultSetTimout;
449 | }
450 | try {
451 | if (typeof clearTimeout === 'function') {
452 | cachedClearTimeout = clearTimeout;
453 | } else {
454 | cachedClearTimeout = defaultClearTimeout;
455 | }
456 | } catch (e) {
457 | cachedClearTimeout = defaultClearTimeout;
458 | }
459 | } ())
460 | function runTimeout(fun) {
461 | if (cachedSetTimeout === setTimeout) {
462 | //normal enviroments in sane situations
463 | return setTimeout(fun, 0);
464 | }
465 | // if setTimeout wasn't available but was latter defined
466 | if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
467 | cachedSetTimeout = setTimeout;
468 | return setTimeout(fun, 0);
469 | }
470 | try {
471 | // when when somebody has screwed with setTimeout but no I.E. maddness
472 | return cachedSetTimeout(fun, 0);
473 | } catch(e){
474 | try {
475 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
476 | return cachedSetTimeout.call(null, fun, 0);
477 | } catch(e){
478 | // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
479 | return cachedSetTimeout.call(this, fun, 0);
480 | }
481 | }
482 |
483 |
484 | }
485 | function runClearTimeout(marker) {
486 | if (cachedClearTimeout === clearTimeout) {
487 | //normal enviroments in sane situations
488 | return clearTimeout(marker);
489 | }
490 | // if clearTimeout wasn't available but was latter defined
491 | if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
492 | cachedClearTimeout = clearTimeout;
493 | return clearTimeout(marker);
494 | }
495 | try {
496 | // when when somebody has screwed with setTimeout but no I.E. maddness
497 | return cachedClearTimeout(marker);
498 | } catch (e){
499 | try {
500 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
501 | return cachedClearTimeout.call(null, marker);
502 | } catch (e){
503 | // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
504 | // Some versions of I.E. have different rules for clearTimeout vs setTimeout
505 | return cachedClearTimeout.call(this, marker);
506 | }
507 | }
508 |
509 |
510 |
511 | }
512 | var queue = [];
513 | var draining = false;
514 | var currentQueue;
515 | var queueIndex = -1;
516 |
517 | function cleanUpNextTick() {
518 | if (!draining || !currentQueue) {
519 | return;
520 | }
521 | draining = false;
522 | if (currentQueue.length) {
523 | queue = currentQueue.concat(queue);
524 | } else {
525 | queueIndex = -1;
526 | }
527 | if (queue.length) {
528 | drainQueue();
529 | }
530 | }
531 |
532 | function drainQueue() {
533 | if (draining) {
534 | return;
535 | }
536 | var timeout = runTimeout(cleanUpNextTick);
537 | draining = true;
538 |
539 | var len = queue.length;
540 | while(len) {
541 | currentQueue = queue;
542 | queue = [];
543 | while (++queueIndex < len) {
544 | if (currentQueue) {
545 | currentQueue[queueIndex].run();
546 | }
547 | }
548 | queueIndex = -1;
549 | len = queue.length;
550 | }
551 | currentQueue = null;
552 | draining = false;
553 | runClearTimeout(timeout);
554 | }
555 |
556 | process.nextTick = function (fun) {
557 | var args = new Array(arguments.length - 1);
558 | if (arguments.length > 1) {
559 | for (var i = 1; i < arguments.length; i++) {
560 | args[i - 1] = arguments[i];
561 | }
562 | }
563 | queue.push(new Item(fun, args));
564 | if (queue.length === 1 && !draining) {
565 | runTimeout(drainQueue);
566 | }
567 | };
568 |
569 | // v8 likes predictible objects
570 | function Item(fun, array) {
571 | this.fun = fun;
572 | this.array = array;
573 | }
574 | Item.prototype.run = function () {
575 | this.fun.apply(null, this.array);
576 | };
577 | process.title = 'browser';
578 | process.browser = true;
579 | process.env = {};
580 | process.argv = [];
581 | process.version = ''; // empty string to avoid regexp issues
582 | process.versions = {};
583 |
584 | function noop() {}
585 |
586 | process.on = noop;
587 | process.addListener = noop;
588 | process.once = noop;
589 | process.off = noop;
590 | process.removeListener = noop;
591 | process.removeAllListeners = noop;
592 | process.emit = noop;
593 | process.prependListener = noop;
594 | process.prependOnceListener = noop;
595 |
596 | process.listeners = function (name) { return [] }
597 |
598 | process.binding = function (name) {
599 | throw new Error('process.binding is not supported');
600 | };
601 |
602 | process.cwd = function () { return '/' };
603 | process.chdir = function (dir) {
604 | throw new Error('process.chdir is not supported');
605 | };
606 | process.umask = function() { return 0; };
607 |
608 |
609 | /***/ }),
610 | /* 4 */
611 | /***/ (function(module, exports, __webpack_require__) {
612 |
613 | "use strict";
614 | /* WEBPACK VAR INJECTION */(function(module) {Object.defineProperty(exports, "__esModule", { value: true });var _extends = Object.assign || function (target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {target[key] = source[key];}}}return target;};(function () {var enterModule = __webpack_require__(0).enterModule;enterModule && enterModule(module);})();var totalMSTPCalcs = 0;
615 | var totalMSTPCache = 0;
616 |
617 | var shallowSubObjectCompare = exports.shallowSubObjectCompare = function shallowSubObjectCompare(obj, subObj, subObjectkeys) {
618 | var len = subObjectkeys.length;
619 | for (var i = 0; i < len; ++i) {
620 | var key = subObjectkeys[i];
621 | if (subObj[key] !== obj[key]) {
622 | return false;
623 | }
624 | }
625 | return true;
626 | };
627 |
628 | var shallowCopy = exports.shallowCopy = function shallowCopy(obj, copykeys) {
629 | if (copykeys === undefined) {
630 | copykeys = Object.keys(obj);
631 | }
632 | var subObj = {};
633 | var len = copykeys.length;
634 | for (var i = 0; i < len; ++i) {
635 | var key = copykeys[i];
636 | subObj[key] = obj[key];
637 | }
638 | return subObj;
639 | };
640 |
641 | var displayMessage = function displayMessage(obj) {
642 | if (!obj.prevState) {
643 | console.log(obj.msg);
644 | } else {
645 | console.log(obj.msg + "\nprevState: %onextState: %o",
646 |
647 | obj.prevState,
648 | obj.nextState);
649 |
650 | }
651 |
652 | // Give the stats as performance feedback to developers.
653 | console.log("totalMSTPCalcs=" + totalMSTPCalcs + ", totalMSTPCache=" + totalMSTPCache);
654 | };
655 |
656 | var mapStateToPropsCache = function mapStateToPropsCache(obj) {
657 | totalMSTPCache++;
658 | displayMessage(obj);
659 | };
660 |
661 | var mapStateToPropsCalc = function mapStateToPropsCalc(obj) {
662 | totalMSTPCalcs++;
663 | displayMessage(obj);
664 | };
665 |
666 | var defaultOptions = exports.defaultOptions = function defaultOptions(options) {
667 | if (options === undefined) {
668 | return;
669 | }
670 | options = _extends({}, options);
671 | if (!options.mapStateToPropsCache) {
672 | options.mapStateToPropsCache = function () {};
673 | }
674 | if (!options.mapStateToPropsCalc) {
675 | options.mapStateToPropsCalc = function () {};
676 | }
677 | if (options.useDefaultMSTPCacheLogging) {
678 | options.mapStateToPropsCalc = mapStateToPropsCalc;
679 | options.mapStateToPropsCache = mapStateToPropsCache;
680 | }
681 | return options;
682 | };;(function () {var reactHotLoader = __webpack_require__(0).default;var leaveModule = __webpack_require__(0).leaveModule;if (!reactHotLoader) {return;}reactHotLoader.register(totalMSTPCalcs, "totalMSTPCalcs", "C:/Users/Andrew/Documents/GitHub/simpler-redux/src/util.js");reactHotLoader.register(totalMSTPCache, "totalMSTPCache", "C:/Users/Andrew/Documents/GitHub/simpler-redux/src/util.js");reactHotLoader.register(shallowSubObjectCompare, "shallowSubObjectCompare", "C:/Users/Andrew/Documents/GitHub/simpler-redux/src/util.js");reactHotLoader.register(shallowCopy, "shallowCopy", "C:/Users/Andrew/Documents/GitHub/simpler-redux/src/util.js");reactHotLoader.register(displayMessage, "displayMessage", "C:/Users/Andrew/Documents/GitHub/simpler-redux/src/util.js");reactHotLoader.register(mapStateToPropsCache, "mapStateToPropsCache", "C:/Users/Andrew/Documents/GitHub/simpler-redux/src/util.js");reactHotLoader.register(mapStateToPropsCalc, "mapStateToPropsCalc", "C:/Users/Andrew/Documents/GitHub/simpler-redux/src/util.js");reactHotLoader.register(defaultOptions, "defaultOptions", "C:/Users/Andrew/Documents/GitHub/simpler-redux/src/util.js");leaveModule(module);})();;
683 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)(module)))
684 |
685 | /***/ }),
686 | /* 5 */
687 | /***/ (function(module, exports) {
688 |
689 | module.exports = require("react");
690 |
691 | /***/ }),
692 | /* 6 */
693 | /***/ (function(module, exports, __webpack_require__) {
694 |
695 | "use strict";
696 | Object.defineProperty(exports, "__esModule", { value: true });var _simplerRedux = __webpack_require__(2);Object.keys(_simplerRedux).forEach(function (key) {if (key === "default" || key === "__esModule") return;Object.defineProperty(exports, key, { enumerable: true, get: function get() {return _simplerRedux[key];} });});var _reactSimplerRedux = __webpack_require__(10);
697 | Object.keys(_reactSimplerRedux).forEach(function (key) {if (key === "default" || key === "__esModule") return;Object.defineProperty(exports, key, { enumerable: true, get: function get() {return _reactSimplerRedux[key];} });});
698 |
699 | /***/ }),
700 | /* 7 */
701 | /***/ (function(module, exports) {
702 |
703 | module.exports = require("redux");
704 |
705 | /***/ }),
706 | /* 8 */
707 | /***/ (function(module, exports, __webpack_require__) {
708 |
709 | "use strict";
710 | function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0});var React=_interopDefault(__webpack_require__(5)),classCallCheck=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},inherits=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)},possibleConstructorReturn=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},AppContainer=function(e){function t(){return classCallCheck(this,t),possibleConstructorReturn(this,e.apply(this,arguments))}return inherits(t,e),t.prototype.render=function(){return React.Children.only(this.props.children)},t}(React.Component),hot_prod=function(){return function(e){return e}},areComponentsEqual=function(e,t){return e===t},setConfig=function(){};exports.AppContainer=AppContainer,exports.hot=hot_prod,exports.areComponentsEqual=areComponentsEqual,exports.setConfig=setConfig;
711 |
712 |
713 | /***/ }),
714 | /* 9 */
715 | /***/ (function(module, exports, __webpack_require__) {
716 |
717 | "use strict";
718 | /* WEBPACK VAR INJECTION */(function(module) {Object.defineProperty(exports, "__esModule", { value: true });(function () {var enterModule = __webpack_require__(0).enterModule;enterModule && enterModule(module);})();function _defineProperty(obj, key, value) {if (key in obj) {Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });} else {obj[key] = value;}return obj;}var proxyDefined = function proxyDefined() {return (
719 | typeof Proxy !== 'undefined');};
720 |
721 | var getReducerKeyValue = function getReducerKeyValue(simplerReduxStore, reducerKey, key) {return (
722 | simplerReduxStore.getRState(reducerKey)[key]);};
723 |
724 | var setReducerKeyValue = function setReducerKeyValue(simplerReduxStore, reducerKey, key, value) {
725 | simplerReduxStore.setRState(reducerKey, _defineProperty({}, key, value));
726 | return true;
727 | };
728 |
729 | var defineProxyGetSet = function defineProxyGetSet(obj, simplerReduxStore, reducerKey, key) {
730 | Object.defineProperty(
731 | obj,
732 | key, {
733 | get: function get() {return (
734 | getReducerKeyValue(simplerReduxStore, reducerKey, key));},
735 | set: function set(value) {return (
736 | setReducerKeyValue(simplerReduxStore, reducerKey, key, value));} });
737 |
738 |
739 | };
740 |
741 | var simulateProxy = function simulateProxy(simplerReduxStore, reducerKey, initialState) {return (
742 | Object.keys(initialState).reduce(function (obj, key) {
743 | defineProxyGetSet(obj, simplerReduxStore, reducerKey, key);
744 | return obj;
745 | }, {}));};
746 |
747 | var getProxyHandler = function getProxyHandler(reducerKey) {
748 | return {
749 | get: function get(simplerReduxStore, key) {return (
750 | getReducerKeyValue(simplerReduxStore, reducerKey, key));},
751 | set: function set(simplerReduxStore, key, value) {return (
752 | setReducerKeyValue(simplerReduxStore, reducerKey, key, value));} };
753 |
754 | };var _default =
755 |
756 | function _default(simplerReduxStore, reducerKey, initialState) {
757 | if (proxyDefined()) {
758 | return new Proxy(simplerReduxStore, getProxyHandler(reducerKey));
759 | }
760 | return simulateProxy(simplerReduxStore, reducerKey, initialState);
761 | };exports.default = _default;;(function () {var reactHotLoader = __webpack_require__(0).default;var leaveModule = __webpack_require__(0).leaveModule;if (!reactHotLoader) {return;}reactHotLoader.register(proxyDefined, 'proxyDefined', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/proxy.js');reactHotLoader.register(getReducerKeyValue, 'getReducerKeyValue', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/proxy.js');reactHotLoader.register(setReducerKeyValue, 'setReducerKeyValue', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/proxy.js');reactHotLoader.register(defineProxyGetSet, 'defineProxyGetSet', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/proxy.js');reactHotLoader.register(simulateProxy, 'simulateProxy', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/proxy.js');reactHotLoader.register(getProxyHandler, 'getProxyHandler', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/proxy.js');reactHotLoader.register(_default, 'default', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/proxy.js');leaveModule(module);})();;
762 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)(module)))
763 |
764 | /***/ }),
765 | /* 10 */
766 | /***/ (function(module, exports, __webpack_require__) {
767 |
768 | "use strict";
769 | /* WEBPACK VAR INJECTION */(function(module, process) {Object.defineProperty(exports, "__esModule", { value: true });exports.connectWithStore = exports.connectLifeCycleComponentWithStore = exports.hookedLifeCycleComponent = exports.buildSelectorsFromUIState = exports.allStateToPropsUsingSelectorList = exports.allStateToPropsUsingSelectors = exports.allStateToProps = exports.allServiceFunctionsToPropsUsingServiceFunctionList = exports.allServiceFunctionsToProps = exports.allServiceFunctionsToPropsWithStore = undefined;var _jsxFileName = 'C:\\Users\\Andrew\\Documents\\GitHub\\simpler-redux\\src\\react-simpler-redux.js';var _createClass = function () {function defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);}}return function (Constructor, protoProps, staticProps) {if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;};}();var _extends = Object.assign || function (target) {for (var i = 1; i < arguments.length; i++) {var source = arguments[i];for (var key in source) {if (Object.prototype.hasOwnProperty.call(source, key)) {target[key] = source[key];}}}return target;};var _react = __webpack_require__(5);var _react2 = _interopRequireDefault(_react);
770 | var _reactRedux = __webpack_require__(11);
771 | var _propTypes = __webpack_require__(12);var _propTypes2 = _interopRequireDefault(_propTypes);
772 | var _hoistNonReactStatics = __webpack_require__(13);var _hoistNonReactStatics2 = _interopRequireDefault(_hoistNonReactStatics);
773 | var _simplerRedux = __webpack_require__(2);
774 | var _util = __webpack_require__(4);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}(function () {var enterModule = __webpack_require__(0).enterModule;enterModule && enterModule(module);})();function _objectWithoutProperties(obj, keys) {var target = {};for (var i in obj) {if (keys.indexOf(i) >= 0) continue;if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;target[i] = obj[i];}return target;}function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}}function _possibleConstructorReturn(self, call) {if (!self) {throw new ReferenceError("this hasn't been initialised - super() hasn't been called");}return call && (typeof call === "object" || typeof call === "function") ? call : self;}function _inherits(subClass, superClass) {if (typeof superClass !== "function" && superClass !== null) {throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);}subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;}function _toConsumableArray(arr) {if (Array.isArray(arr)) {for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {arr2[i] = arr[i];}return arr2;} else {return Array.from(arr);}}
775 |
776 | // React lifecycle events supported in the model code.
777 | var reactLifeCycleEvents = {
778 | onConstructor: 'onConstructor',
779 | onRender: 'onRender',
780 | componentDidMount: 'componentDidMount',
781 | componentWillUnmount: 'componentWillUnmount',
782 | componentDidCatch: 'componentDidCatch',
783 | componentToRender: 'componentToRender'
784 |
785 |
786 | //
787 | // Builds a mapDispatchToProps function based on the service functions and adds the store as
788 | // the first parameter to each service function and the rest of the parameters given
789 | // by the react UI component when called in react.
790 | //
791 | };var allServiceFunctionsToPropsWithStore = exports.allServiceFunctionsToPropsWithStore = function allServiceFunctionsToPropsWithStore(serviceFunctions) {
792 | var keys = Object.keys(serviceFunctions);
793 | return function (_dispatch, ownProps) {return (
794 | keys.reduce(function (obj, e) {
795 | obj[e] = function () {for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {args[_key] = arguments[_key];}return serviceFunctions[e].apply(serviceFunctions, [ownProps.store].concat(args));};
796 | return obj;
797 | }, {}));};
798 | };
799 |
800 | //
801 | // Builds a mapDispatchToProps function based on the service functions and adds
802 | // all of the parameters given by the react UI component when called in react.
803 | //
804 | var allServiceFunctionsToProps = exports.allServiceFunctionsToProps = function allServiceFunctionsToProps(serviceFunctions) {return (
805 | function () {return _extends({}, serviceFunctions);});};
806 |
807 | //
808 | // Builds a mapDispatchToProps function based on a serviceFunctionList object.
809 | // This allows including service functions from various modules.
810 | // The keylist allows selecting only a subset of a module's service functions.
811 | // If keylist is not specified then all service functions will be included.
812 | //
813 | var allServiceFunctionsToPropsUsingServiceFunctionList = exports.allServiceFunctionsToPropsUsingServiceFunctionList = function allServiceFunctionsToPropsUsingServiceFunctionList(serviceFunctionList) {
814 | serviceFunctionList = [].concat(_toConsumableArray(serviceFunctionList));
815 | return function (_dispatch, ownProps) {return (
816 | serviceFunctionList.reduce(function (obj, e) {
817 | var keylist = e.keylist ? e.keylist : Object.keys(e.serviceFunctions);
818 | keylist.forEach(function (key) {
819 | if (e.withStore) {
820 | obj[key] = function () {var _e$serviceFunctions;for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {args[_key2] = arguments[_key2];}return (_e$serviceFunctions = e.serviceFunctions)[key].apply(_e$serviceFunctions, [ownProps.store].concat(args));};
821 | } else {
822 | obj[key] = function () {var _e$serviceFunctions2;return (_e$serviceFunctions2 = e.serviceFunctions)[key].apply(_e$serviceFunctions2, arguments);};
823 | }
824 | });
825 | return obj;
826 | }, {}));};
827 | };
828 |
829 | //
830 | // Builds a mapStateToProps function that returns the entire reducer state.
831 | //
832 | var allStateToProps = exports.allStateToProps = function allStateToProps(reducerKey) {
833 | return function (state) {
834 | if (process.env.NODE_ENV !== 'production') {
835 | _simplerRedux.srOptions.mapStateToPropsCache({
836 | msg: 'mapStateToProps fast calculation at ' + reducerKey + ', cache not needed.',
837 | reducerKey: reducerKey });
838 |
839 | }
840 | return state[reducerKey];
841 | };
842 | };
843 |
844 | //
845 | // Builds a mapStateToProps function based on a selectors object.
846 | //
847 | var allStateToPropsUsingSelectors = exports.allStateToPropsUsingSelectors = function allStateToPropsUsingSelectors(selectors, reducerKey) {
848 | var keys = Object.keys(selectors);
849 | if (!reducerKey) {
850 | return (
851 | function (state) {return (
852 | keys.reduce(function (obj, e) {
853 | obj[e] = selectors[e](state);
854 | return obj;
855 | }, {}));});
856 |
857 | }
858 | // Implements a caching mechanism below such that if the state at the reducerKey did not change
859 | // then we return the previously calculated object.
860 | var prevState = void 0;
861 | var lastresult = void 0;
862 | return function (state) {
863 | if (prevState !== state[reducerKey]) {
864 | if (process.env.NODE_ENV !== 'production') {
865 | _simplerRedux.srOptions.mapStateToPropsCalc({
866 | msg: 'selector calculated ' + reducerKey,
867 | prevState: prevState,
868 | nextState: state[reducerKey],
869 | reducerKey: reducerKey });
870 |
871 | }
872 | prevState = state[reducerKey];
873 | lastresult = keys.reduce(function (obj, e) {
874 | obj[e] = selectors[e](state);
875 | return obj;
876 | }, {});
877 | } else {
878 | if (process.env.NODE_ENV !== 'production') {
879 | _simplerRedux.srOptions.mapStateToPropsCache({
880 | msg: 'selector cache ' + reducerKey,
881 | prevState: prevState,
882 | nextState: state[reducerKey],
883 | reducerKey: reducerKey });
884 |
885 | }
886 | }
887 | return lastresult;
888 | };
889 | };
890 |
891 | //
892 | // Builds a mapStateToProps function based on a selectorList object.
893 | // This allows including selectors from various modules.
894 | // The keylist allows selecting only a subset of a module's selectors.
895 | // If keylist is not specified then all selectors will be included.
896 | //
897 | var allStateToPropsUsingSelectorList = exports.allStateToPropsUsingSelectorList = function allStateToPropsUsingSelectorList(selectorList, componentName) {
898 | selectorList = [].concat(_toConsumableArray(selectorList));
899 | var useCache = true;
900 | var reducerKeys = [];
901 | for (var i = 0; i < selectorList.length; ++i) {
902 | if (selectorList[i].keylist === undefined) {
903 | selectorList[i].keylist = Object.keys(selectorList[i].selectors);
904 | }
905 | var reducerKey = selectorList[i].reducerKey;
906 | if (reducerKey) {
907 | reducerKeys.push(reducerKey);
908 | } else {
909 | useCache = false;
910 | }
911 | }
912 | if (componentName === undefined) {
913 | componentName = reducerKeys.toString();
914 | }
915 | if (useCache) {
916 | var prevStates = {};
917 | var lastResult = void 0;
918 | return function (state) {
919 | if (!(0, _util.shallowSubObjectCompare)(state, prevStates, reducerKeys)) {
920 | if (process.env.NODE_ENV !== 'production') {
921 | _simplerRedux.srOptions.mapStateToPropsCalc({
922 | msg: 'Selector List calculated ' + componentName + '.',
923 | nextState: (0, _util.shallowCopy)(state, reducerKeys),
924 | prevState: prevStates,
925 | reducerKeys: reducerKeys });
926 |
927 | }
928 | prevStates = (0, _util.shallowCopy)(state, reducerKeys);
929 | lastResult = selectorList.reduce(function (obj, e) {
930 | e.keylist.forEach(function (key) {
931 | obj[key] = e.selectors[key](state);
932 | });
933 | return obj;
934 | }, {});
935 | } else {
936 | if (process.env.NODE_ENV !== 'production') {
937 | _simplerRedux.srOptions.mapStateToPropsCache({
938 | msg: 'Selector List cache ' + componentName + '.',
939 | nextState: (0, _util.shallowCopy)(state, reducerKeys),
940 | prevState: prevStates,
941 | reducerKeys: reducerKeys });
942 |
943 | }
944 | }
945 | return lastResult;
946 | };
947 | }
948 | reducerKeys = [];
949 | return function (state) {return (
950 | selectorList.reduce(function (obj, e) {
951 | e.keylist.forEach(function (key) {
952 | obj[key] = e.selectors[key](state);
953 | });
954 | return obj;
955 | }, {}));};
956 | };
957 |
958 | //
959 | // Builds a model selectors object from either initialUIState or initialState.
960 | // initialUIState should only contain keys that you want in the
961 | // props of the react component.
962 | // This is not for specialized selectors for the UI that require conjunctions or
963 | // selectors from other modules, etc.
964 | // It is only for simple selectors of the nature state => state[reducerKey][stateKey]
965 | //
966 | var buildSelectorsFromUIState = exports.buildSelectorsFromUIState = function buildSelectorsFromUIState(reducerKey, initialState) {
967 | var keys = Object.keys(initialState);
968 | return keys.reduce(function (obj, e) {
969 | obj[e] = function (state) {return state[reducerKey][e];};
970 | return obj;
971 | }, {});
972 | };
973 |
974 | //
975 | // Builds a mapStateToProps function that returns key/UI State pairs
976 | //
977 | var allStateToPropsUsingUIState = function allStateToPropsUsingUIState(reducerKey, initialUIState) {
978 | var keys = Object.keys(initialUIState);
979 | var prevState = void 0;
980 | var lastResult = void 0;
981 | return function (state) {
982 | if (state[reducerKey] !== prevState) {
983 | if (process.env.NODE_ENV !== 'production') {
984 | _simplerRedux.srOptions.mapStateToPropsCalc({
985 | msg: 'State props calculated at ' + reducerKey + '.',
986 | nextState: state[reducerKey],
987 | prevState: prevState,
988 | reducerKey: reducerKey });
989 |
990 | }
991 | prevState = state[reducerKey];
992 | lastResult = (0, _util.shallowCopy)(state[reducerKey], keys);
993 | } else {
994 | if (process.env.NODE_ENV !== 'production') {
995 | _simplerRedux.srOptions.mapStateToPropsCache({
996 | msg: 'State props cache at ' + reducerKey + '.',
997 | nextState: state[reducerKey],
998 | prevState: prevState,
999 | reducerKey: reducerKey });
1000 |
1001 | }
1002 | }
1003 | return lastResult;
1004 | };
1005 | };
1006 |
1007 | var buildCachedMapStateToProps = function buildCachedMapStateToProps(mapStateToProps, mapStateToPropsReducerKeys, componentName) {
1008 | if (!Array.isArray(mapStateToPropsReducerKeys)) {
1009 | mapStateToPropsReducerKeys = [mapStateToPropsReducerKeys];
1010 | }
1011 | if (componentName === undefined) {
1012 | componentName = mapStateToPropsReducerKeys.toString();
1013 | }
1014 | var prevStates = {};
1015 | var lastResult = void 0;
1016 | return function (state) {
1017 | if (!(0, _util.shallowSubObjectCompare)(state, prevStates, mapStateToPropsReducerKeys)) {
1018 | if (process.env.NODE_ENV !== 'production') {
1019 | _simplerRedux.srOptions.mapStateToPropsCalc({
1020 | msg: 'mapStateToProps calculated ' + componentName + '.',
1021 | nextState: (0, _util.shallowCopy)(state, mapStateToPropsReducerKeys),
1022 | prevState: prevStates,
1023 | reducerKeys: mapStateToPropsReducerKeys });
1024 |
1025 | }
1026 | prevStates = (0, _util.shallowCopy)(state, mapStateToPropsReducerKeys);
1027 | lastResult = mapStateToProps(state);
1028 | } else {
1029 | if (process.env.NODE_ENV !== 'production') {
1030 | _simplerRedux.srOptions.mapStateToPropsCache({
1031 | msg: 'mapStateToProps cache ' + componentName + '.',
1032 | nextState: (0, _util.shallowCopy)(state, mapStateToPropsReducerKeys),
1033 | prevState: prevStates,
1034 | reducerKeys: mapStateToPropsReducerKeys });
1035 |
1036 | }
1037 | }
1038 | return lastResult;
1039 | };
1040 | };
1041 |
1042 | var connectWithStoreBase = function connectWithStoreBase(
1043 | options)
1044 | {var
1045 |
1046 | uiComponent =
1047 |
1048 |
1049 |
1050 |
1051 |
1052 |
1053 |
1054 |
1055 |
1056 |
1057 |
1058 |
1059 |
1060 |
1061 |
1062 |
1063 | options.uiComponent,reduxOptions = options.reduxOptions,storeIsDefinedCallback = options.storeIsDefinedCallback,reducerKey = options.reducerKey,isDynamicReducer = options.isDynamicReducer,initialState = options.initialState,mapStateToProps = options.mapStateToProps,mapDispatchToProps = options.mapDispatchToProps,serviceFunctions = options.serviceFunctions,serviceFunctionList = options.serviceFunctionList,selectors = options.selectors,selectorList = options.selectorList,initialUIState = options.initialUIState,noStoreParameterOnServiceFunctions = options.noStoreParameterOnServiceFunctions,mergeProps = options.mergeProps,mapStateToPropsReducerKeys = options.mapStateToPropsReducerKeys,componentName = options.componentName;
1064 |
1065 | if (process.env.NODE_ENV !== 'production') {
1066 | if (uiComponent === undefined) {
1067 | throw new Error('connectWithStore: options.uiComponent cannot be undefined, reducerKey=' + reducerKey + '.');
1068 | }
1069 | if (storeIsDefinedCallback && typeof storeIsDefinedCallback !== 'function') {
1070 | throw new Error('connectWithStore: options.storeIsDefinedCallback must be a function, reducerKey=' + reducerKey + '.');
1071 | }
1072 | if (selectors !== undefined && initialUIState !== undefined) {
1073 | throw new Error('connectWithStore: Cannot export both selectors and initialUIState, reducerKey=' + reducerKey + '.');
1074 | }
1075 | var displayName = '';
1076 | if (uiComponent !== undefined) {
1077 | if (uiComponent.displayName !== undefined) {
1078 | displayName = uiComponent.displayName;
1079 | } else {
1080 | displayName = uiComponent.name;
1081 | }
1082 | }
1083 | if (selectorList !== undefined) {
1084 | selectorList.forEach(function (e) {
1085 | if (e.keylist !== undefined) {
1086 | e.keylist.forEach(function (key) {
1087 | if (typeof e.selectors[key] !== 'function') {
1088 | throw new Error('connectWithStore ' + displayName + ': The selectors key ' + key + ' is not in the selectors ' + e.keylist.toString() + '.');
1089 | }
1090 | });
1091 | }
1092 | });
1093 | }
1094 | if (serviceFunctionList !== undefined) {
1095 | serviceFunctionList.forEach(function (e) {
1096 | if (e.keylist !== undefined) {
1097 | e.keylist.forEach(function (key) {
1098 | if (typeof e.serviceFunctions[key] !== 'function') {
1099 | throw new Error('connectWithStore ' + displayName + ': The serviceFunctions key ' + key + ' is not in the serviceFunctionList ' + e.keylist.toString() + '.');
1100 | }
1101 | });
1102 | }
1103 | });
1104 | }
1105 | }
1106 |
1107 | if (componentName === undefined) {
1108 | componentName = reducerKey;
1109 | }
1110 |
1111 | // Default initialState (reducer state) to initialUIState (component props state).
1112 | if (initialState === undefined) {
1113 | initialState = initialUIState;
1114 | }
1115 |
1116 | // Default initialUIState (component props state) to initialState (reducer state).
1117 | if (initialUIState === undefined) {
1118 | initialUIState = initialState;
1119 | }
1120 |
1121 | var withRef = reduxOptions && reduxOptions.withRef;
1122 | if (initialState !== undefined) {
1123 | initialState = _extends({}, initialState);
1124 | }
1125 |
1126 | // If mapStateToProps is defined by the consumer then keep it no matter what.
1127 | if (mapStateToProps !== undefined) {
1128 | if (mapStateToPropsReducerKeys !== undefined) {
1129 | mapStateToProps = buildCachedMapStateToProps(mapStateToProps, mapStateToPropsReducerKeys, componentName);
1130 | }
1131 | } else {
1132 | if (selectorList !== undefined) {
1133 | mapStateToProps = allStateToPropsUsingSelectorList(selectorList, componentName);
1134 | } else if (selectors !== undefined) {
1135 | mapStateToProps = allStateToPropsUsingSelectors(selectors, reducerKey);
1136 | } else if (reducerKey !== undefined && initialUIState !== undefined) {
1137 | // This is for efficiency. initialUIState and initialState are the same so
1138 | // mapStateToProps simply returns the entire reducerKey state.
1139 | if (Object.keys(initialUIState).length === Object.keys(initialState).length) {
1140 | mapStateToProps = allStateToProps(reducerKey);
1141 | } else {
1142 | mapStateToProps = allStateToPropsUsingUIState(reducerKey, initialUIState);
1143 | }
1144 | }
1145 | }
1146 |
1147 | // If mapDispatchToProps is defined by the consumer then keep it no matter what.
1148 | if (mapDispatchToProps === undefined) {
1149 | if (serviceFunctionList !== undefined) {
1150 | mapDispatchToProps = allServiceFunctionsToPropsUsingServiceFunctionList(serviceFunctionList);
1151 | } else if (serviceFunctions !== undefined) {
1152 | if (noStoreParameterOnServiceFunctions) {
1153 | mapDispatchToProps = allServiceFunctionsToProps(serviceFunctions);
1154 | } else {
1155 | mapDispatchToProps = allServiceFunctionsToPropsWithStore(serviceFunctions);
1156 | }
1157 | }
1158 | }
1159 |
1160 | // Call the react-redux connect.
1161 | var ConnectedComponent = (0, _reactRedux.connect)(
1162 | mapStateToProps,
1163 | mapDispatchToProps,
1164 | mergeProps,
1165 | reduxOptions)(
1166 | uiComponent);var
1167 |
1168 | HOC = function (_React$Component) {_inherits(HOC, _React$Component);
1169 | function HOC(props, context) {_classCallCheck(this, HOC);
1170 |
1171 | // Handles the dynamic loading of the reducer.
1172 | var _this = _possibleConstructorReturn(this, (HOC.__proto__ || Object.getPrototypeOf(HOC)).call(this, props, context));if (isDynamicReducer !== false && _this.context.store.isDynamicReducerLoading()) {
1173 | // This will build the reducer and add it to the reducers object.
1174 | if (reducerKey !== undefined && initialState !== undefined) {
1175 | _this.context.store.addReducer(reducerKey, initialState);
1176 | }
1177 | }
1178 | // Handles a callback for the consumer to cache and/or use the store.
1179 | if (storeIsDefinedCallback) {
1180 | storeIsDefinedCallback(_this.context.store, _simplerRedux.stateAccessors);
1181 | }
1182 | _this.setWrappedInstance = _this.setWrappedInstance.bind(_this);return _this;
1183 | }
1184 | // Support the redux connect getWrappedInstance out of this HOC.
1185 | // This way, the consumer does nothing different when using this HOC
1186 | // vs redux connected components when handling refs.
1187 | _createClass(HOC, [{ key: 'setWrappedInstance', value: function setWrappedInstance(ref) {
1188 | if (!withRef) {
1189 | return;
1190 | }
1191 | // Refer to the original instance of the component wrapped with connect.
1192 | if (ref) {
1193 | if (process.env.NODE_ENV !== 'production') {
1194 | if (typeof ref.getWrappedInstance !== 'function') {
1195 | console.log('There is something wrong with redux connect.');
1196 | return;
1197 | }
1198 | }
1199 | this.wrappedInstance = ref.getWrappedInstance();
1200 | }
1201 | } }, { key: 'getWrappedInstance', value: function getWrappedInstance()
1202 | {
1203 | if (process.env.NODE_ENV !== 'production') {
1204 | if (this.wrappedInstance === undefined) {
1205 | console.log('The getWrappedInstance return is undefined. Did you use the withRef: true option?');
1206 | }
1207 | }
1208 | return this.wrappedInstance;
1209 | } }, { key: 'render', value: function render()
1210 | {
1211 | // Add the store to the props of the redux connected component so that it can be referenced
1212 | // in mapDispatchToProps with ownProps.
1213 | return (
1214 | _react2.default.createElement(ConnectedComponent, _extends({},
1215 | this.props, {
1216 | ref: this.setWrappedInstance,
1217 | store: this.context.store, __source: { fileName: _jsxFileName, lineNumber: 446 } })));
1218 |
1219 |
1220 | } }, { key: '__reactstandin__regenerateByEval', // @ts-ignore
1221 | value: function __reactstandin__regenerateByEval(key, code) {// @ts-ignore
1222 | this[key] = eval(code);} }]);return HOC;}(_react2.default.Component);
1223 | HOC.displayName = 'connectWithStore(' + (ConnectedComponent.displayName || ConnectedComponent.name) + ')';
1224 | // Opt in for the context.
1225 | HOC.contextTypes = {
1226 | store: _propTypes2.default.object };
1227 |
1228 | return (0, _hoistNonReactStatics2.default)(HOC, ConnectedComponent);
1229 | };
1230 |
1231 | //
1232 | // This supports moving the react life cycle events into the model/business code serviceFunctions functions object.
1233 | //
1234 | var ReactLifeCycleComponent = function (_React$Component2) {_inherits(ReactLifeCycleComponent, _React$Component2);
1235 | function ReactLifeCycleComponent(props) {_classCallCheck(this, ReactLifeCycleComponent);var _this2 = _possibleConstructorReturn(this, (ReactLifeCycleComponent.__proto__ || Object.getPrototypeOf(ReactLifeCycleComponent)).call(this,
1236 | props));
1237 | _this2.runFunction = _this2.runFunction.bind(_this2);
1238 | _this2.runFunction(_this2.props.onConstructor);return _this2;
1239 | }_createClass(ReactLifeCycleComponent, [{ key: 'runFunction', value: function runFunction(
1240 | func) {var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
1241 | return func ? func.call.apply(func, [this].concat(_toConsumableArray(args))) : null;
1242 | } }, { key: 'componentDidMount', value: function componentDidMount()
1243 | {
1244 | this.runFunction(this.props.componentDidMount);
1245 | } }, { key: 'componentWillUnmount', value: function componentWillUnmount()
1246 | {
1247 | this.runFunction(this.props.componentWillUnmount);
1248 | } }, { key: 'componentDidCatch', value: function componentDidCatch()
1249 | {for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {args[_key3] = arguments[_key3];}
1250 | this.runFunction(this.props.componentDidCatch, args);
1251 | } }, { key: 'render', value: function render()
1252 | {
1253 | this.runFunction(this.props.onRender);
1254 | // Render prop
1255 | return this.props.componentToRender();
1256 | } }, { key: '__reactstandin__regenerateByEval', // @ts-ignore
1257 | value: function __reactstandin__regenerateByEval(key, code) {// @ts-ignore
1258 | this[key] = eval(code);} }]);return ReactLifeCycleComponent;}(_react2.default.Component);
1259 | ReactLifeCycleComponent.propTypes = {
1260 | onConstructor: _propTypes2.default.func,
1261 | onRender: _propTypes2.default.func,
1262 | componentDidMount: _propTypes2.default.func,
1263 | componentWillUnmount: _propTypes2.default.func,
1264 | componentDidCatch: _propTypes2.default.func,
1265 | componentToRender: _propTypes2.default.func.isRequired };
1266 |
1267 |
1268 | var hookedLifeCycleComponent = function hookedLifeCycleComponent(Component) {var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};var
1269 |
1270 | onConstructor =
1271 |
1272 |
1273 |
1274 |
1275 |
1276 |
1277 | props.onConstructor,onRender = props.onRender,componentDidMount = props.componentDidMount,componentWillUnmount = props.componentWillUnmount,componentDidCatch = props.componentDidCatch,componentToRender = props.componentToRender,propsToPass = _objectWithoutProperties(props, ['onConstructor', 'onRender', 'componentDidMount', 'componentWillUnmount', 'componentDidCatch', 'componentToRender']);
1278 | return (
1279 | _react2.default.createElement(ReactLifeCycleComponent, {
1280 | onConstructor: onConstructor,
1281 | onRender: onRender,
1282 | componentDidMount: componentDidMount,
1283 | componentWillUnmount: componentWillUnmount,
1284 | componentDidCatch: componentDidCatch,
1285 | componentToRender: function componentToRender() {return _react2.default.createElement(Component, _extends({}, propsToPass, { __source: { fileName: _jsxFileName, lineNumber: 517 } }));}, __source: { fileName: _jsxFileName, lineNumber: 511 } }));
1286 |
1287 |
1288 | };exports.hookedLifeCycleComponent = hookedLifeCycleComponent;
1289 |
1290 | var connectLifeCycleComponentWithStore = exports.connectLifeCycleComponentWithStore = function connectLifeCycleComponentWithStore(options) {
1291 | if (process.env.NODE_ENV !== 'production') {
1292 | if (options.serviceFunctions === undefined) {
1293 | throw new Error('connectLifeCycleComponentWithStore: You must define and export a serviceFunctions object in the model code in order to use this function.');
1294 | }
1295 | }
1296 | var component = options.uiComponent;
1297 | var hookedLCC = function hookedLCC(props) {return hookedLifeCycleComponent(component, props);};
1298 | var newOptions = _extends({}, options, { uiComponent: hookedLCC });
1299 | return connectWithStoreBase(newOptions);
1300 | };
1301 |
1302 | /*
1303 | options object parameter
1304 |
1305 | reducerKey - (required) The key in the redux store for this module
1306 | initialState - (required) The initial state that will be used in the reducer for initialization.
1307 | initialUIState - (optional) If this is specified then simpler-redux will build a mapStateToProps
1308 | function based on the keys in this object.
1309 | selectors - (optional) If this is specified then simpler-redux will build a mapStateToProps
1310 | function based on the selectors object.
1311 | selectorList - (Optionsl) An array of {selectors, keylist[list of selector keys]}. This allows
1312 | combining selectors from different modules into one in order to build a mapStateToProps that
1313 | includes key/values from other reducers keys including the component reducer selectors. If you
1314 | specify keylist then you can include only a subset of the selectors indtead of all of them.
1315 | serviceFunctions - (optional) If this is specified then simpler-redux will build a mapDispatchToProps
1316 | function based on the keys in this object. These will be the service functions exposed to the
1317 | the react component in the props.
1318 | serviceFunctionList` - An array of {serviceFunctions, keylist[list of serviceFunctions keys],
1319 | withStore}. This allows combining serviceFunctions from different modules into one in order
1320 | to build a mapDispatchToProps that includes key/values from other module serviceFunctions.
1321 | The keylist allows you to select only a subset of the associated service functions. The withStore
1322 | set to true will cause the store to be the first parameter for all the service functions when
1323 | called with the UI parameters following after.
1324 | noStoreParameterOnServiceFunctions = true (Optional) - By default, simpler-redux injects the store as the
1325 | first parameter when any service function is called by the UI. The UI parameters follow.
1326 | If this is set to true then simpler-redux will not do this store injection.
1327 | storeIsDefinedCallback(store, stateAccessors) - (Optional) If this is specified then simpler-redux will call
1328 | this function with the simpler redux store as a parameter when the store becomes available to the react
1329 | component. Use this to call the simpler-redux stateAccessors in order to gain access to
1330 | setState, getState and reducerState.
1331 | Example:
1332 | let setState, reducerState
1333 | export const storeIsDefinedCallback = (store, stateAccessors) =>
1334 | ({setState, reducerState} = stateAccessors(store, reducerKey, initialState))
1335 | isDynamicReducer - (Optional) This supports dynamic reducer loading. For this, simpler-redux
1336 | automatically takes care of building the reducer and loading it into the reducers object.
1337 |
1338 | Note: If you present any redux state in the react component then you must define and export either
1339 | a selectors object or an initialUIState object. Otherwise, you will not have any state in
1340 | the props of the react component.
1341 | */
1342 | var connectWithStore = exports.connectWithStore = function connectWithStore(options) {
1343 | // First decide if the serviceFunctions object contains react lifecycle calls.
1344 | if (options.serviceFunctions !== undefined) {
1345 | var hasLifeCycle = Object.keys(options.serviceFunctions).some(function (e) {return (
1346 | reactLifeCycleEvents[e] !== undefined);});
1347 |
1348 | if (hasLifeCycle) {
1349 | return connectLifeCycleComponentWithStore(options);
1350 | }
1351 | }
1352 | // No react lifecycle calls.
1353 | return connectWithStoreBase(options);
1354 | };;(function () {var reactHotLoader = __webpack_require__(0).default;var leaveModule = __webpack_require__(0).leaveModule;if (!reactHotLoader) {return;}reactHotLoader.register(reactLifeCycleEvents, 'reactLifeCycleEvents', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(allServiceFunctionsToPropsWithStore, 'allServiceFunctionsToPropsWithStore', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(allServiceFunctionsToProps, 'allServiceFunctionsToProps', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(allServiceFunctionsToPropsUsingServiceFunctionList, 'allServiceFunctionsToPropsUsingServiceFunctionList', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(allStateToProps, 'allStateToProps', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(allStateToPropsUsingSelectors, 'allStateToPropsUsingSelectors', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(allStateToPropsUsingSelectorList, 'allStateToPropsUsingSelectorList', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(buildSelectorsFromUIState, 'buildSelectorsFromUIState', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(allStateToPropsUsingUIState, 'allStateToPropsUsingUIState', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(buildCachedMapStateToProps, 'buildCachedMapStateToProps', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(connectWithStoreBase, 'connectWithStoreBase', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(ReactLifeCycleComponent, 'ReactLifeCycleComponent', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(hookedLifeCycleComponent, 'hookedLifeCycleComponent', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(connectLifeCycleComponentWithStore, 'connectLifeCycleComponentWithStore', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');reactHotLoader.register(connectWithStore, 'connectWithStore', 'C:/Users/Andrew/Documents/GitHub/simpler-redux/src/react-simpler-redux.js');leaveModule(module);})();;
1355 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)(module), __webpack_require__(3)))
1356 |
1357 | /***/ }),
1358 | /* 11 */
1359 | /***/ (function(module, exports) {
1360 |
1361 | module.exports = require("react-redux");
1362 |
1363 | /***/ }),
1364 | /* 12 */
1365 | /***/ (function(module, exports) {
1366 |
1367 | module.exports = require("prop-types");
1368 |
1369 | /***/ }),
1370 | /* 13 */
1371 | /***/ (function(module, exports, __webpack_require__) {
1372 |
1373 | /**
1374 | * Copyright 2015, Yahoo! Inc.
1375 | * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
1376 | */
1377 | (function (global, factory) {
1378 | true ? module.exports = factory() :
1379 | typeof define === 'function' && define.amd ? define(factory) :
1380 | (global.hoistNonReactStatics = factory());
1381 | }(this, (function () {
1382 | 'use strict';
1383 |
1384 | var REACT_STATICS = {
1385 | childContextTypes: true,
1386 | contextTypes: true,
1387 | defaultProps: true,
1388 | displayName: true,
1389 | getDefaultProps: true,
1390 | getDerivedStateFromProps: true,
1391 | mixins: true,
1392 | propTypes: true,
1393 | type: true
1394 | };
1395 |
1396 | var KNOWN_STATICS = {
1397 | name: true,
1398 | length: true,
1399 | prototype: true,
1400 | caller: true,
1401 | callee: true,
1402 | arguments: true,
1403 | arity: true
1404 | };
1405 |
1406 | var defineProperty = Object.defineProperty;
1407 | var getOwnPropertyNames = Object.getOwnPropertyNames;
1408 | var getOwnPropertySymbols = Object.getOwnPropertySymbols;
1409 | var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
1410 | var getPrototypeOf = Object.getPrototypeOf;
1411 | var objectPrototype = getPrototypeOf && getPrototypeOf(Object);
1412 |
1413 | return function hoistNonReactStatics(targetComponent, sourceComponent, blacklist) {
1414 | if (typeof sourceComponent !== 'string') { // don't hoist over string (html) components
1415 |
1416 | if (objectPrototype) {
1417 | var inheritedComponent = getPrototypeOf(sourceComponent);
1418 | if (inheritedComponent && inheritedComponent !== objectPrototype) {
1419 | hoistNonReactStatics(targetComponent, inheritedComponent, blacklist);
1420 | }
1421 | }
1422 |
1423 | var keys = getOwnPropertyNames(sourceComponent);
1424 |
1425 | if (getOwnPropertySymbols) {
1426 | keys = keys.concat(getOwnPropertySymbols(sourceComponent));
1427 | }
1428 |
1429 | for (var i = 0; i < keys.length; ++i) {
1430 | var key = keys[i];
1431 | if (!REACT_STATICS[key] && !KNOWN_STATICS[key] && (!blacklist || !blacklist[key])) {
1432 | var descriptor = getOwnPropertyDescriptor(sourceComponent, key);
1433 | try { // Avoid failures from read-only properties
1434 | defineProperty(targetComponent, key, descriptor);
1435 | } catch (e) {}
1436 | }
1437 | }
1438 |
1439 | return targetComponent;
1440 | }
1441 |
1442 | return targetComponent;
1443 | };
1444 | })));
1445 |
1446 |
1447 | /***/ })
1448 | /******/ ]);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simpler-redux",
3 | "version": "0.1.17",
4 | "description": "Redux made simple",
5 | "main": "lib/simpler-redux.js",
6 | "scripts": {
7 | "test:lib": "mocha --reporter spec --require babel-register ./test/**/*.js",
8 | "test": "cross-env NODE_ENV=mochaTesting mocha --require babel-register --require test/setup.js test/projectsetup.js test/mountapp.jsx test/test.js test/testApp.js --no-timeouts",
9 | "builddll:dev": "webpack --colors --config devtools/webpack.config.dlllib.dev.js",
10 | "build:lib": "webpack --config devtools/webpack.config.lib.js",
11 | "build": "npm run build:lib && npm run builddll:dev",
12 | "lint": "eslint src/**",
13 | "start": "cross-env NODE_ENV=development node devtools/devServer.js"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/AndrewBanks10/simpler-redux"
18 | },
19 | "keywords": [
20 | "simpler-redux",
21 | "library",
22 | "redux",
23 | "react"
24 | ],
25 | "author": {
26 | "name": "Andrew Banks",
27 | "email": "andrewbanks10@gmail.com"
28 | },
29 | "license": "MIT",
30 | "homepage": "https://github.com/AndrewBanks10/simpler-redux",
31 | "devDependencies": {
32 | "babel-cli": "^6.24.1",
33 | "babel-core": "^6.25.0",
34 | "babel-eslint": "^8.2.3",
35 | "babel-loader": "^7.1.1",
36 | "babel-preset-env": "^1.6.1",
37 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
38 | "babel-plugin-transform-react-jsx": "^6.24.1",
39 | "babel-plugin-transform-react-jsx-source": "^6.22.0",
40 | "babel-preset-react": "^6.24.1",
41 | "babel-preset-react-optimize": "^1.0.1",
42 | "babel-preset-stage-0": "^6.24.1",
43 | "babel-register": "^6.24.1",
44 | "copyfiles": "^1.2.0",
45 | "cross-env": "^5.0.1",
46 | "enzyme": "^3.3.0",
47 | "enzyme-adapter-react-16": "^1.1.1",
48 | "eslint": "^4.19.1",
49 | "eslint-config-standard": "^11.0.0",
50 | "eslint-config-standard-react": "^5.0.0",
51 | "eslint-import-resolver-webpack": "^0.9.0",
52 | "eslint-plugin-babel": "^5.1.0",
53 | "eslint-plugin-import": "^2.11.0",
54 | "eslint-plugin-lodash": "^2.7.0",
55 | "eslint-plugin-node": "^6.0.1",
56 | "eslint-plugin-react": "^7.5.1",
57 | "eslint-plugin-promise": "^3.7.0",
58 | "eslint-plugin-standard": "^3.1.0",
59 | "ghooks": "^2.0.0",
60 | "html-webpack-plugin": "^2.29.0",
61 | "jsdom": "^11.10.0",
62 | "lodash": "^4.17.4",
63 | "mocha": "^3.4.2",
64 | "react-dom": "^16.3.2",
65 | "react-hot-loader": "^4.1.3",
66 | "react-test-renderer": "^16.3.2",
67 | "rimraf": "^2.6.1",
68 | "webpack": "^3.0.0",
69 | "webpack-dev-server": "^2.5.0"
70 | },
71 | "dependencies": {
72 | "object-assign": "^4.1.1",
73 | "redux": "^3.7.2",
74 | "react": "^16.3.2",
75 | "react-redux": "^5.0.6",
76 | "hoist-non-react-statics": "^2.5.0"
77 | },
78 | "config": {
79 | "host": "localhost",
80 | "port": 3000,
81 | "ghooks": {
82 | "pre-commit": "npm run lint"
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export * from './simpler-redux'
2 | export * from './react-simpler-redux'
3 |
--------------------------------------------------------------------------------
/src/proxy.js:
--------------------------------------------------------------------------------
1 | const proxyDefined = () =>
2 | typeof Proxy !== 'undefined'
3 |
4 | const getReducerKeyValue = (simplerReduxStore, reducerKey, key) =>
5 | simplerReduxStore.getRState(reducerKey)[key]
6 |
7 | const setReducerKeyValue = (simplerReduxStore, reducerKey, key, value) => {
8 | simplerReduxStore.setRState(reducerKey, { [key]: value })
9 | return true
10 | }
11 |
12 | const defineProxyGetSet = (obj, simplerReduxStore, reducerKey, key) => {
13 | Object.defineProperty(
14 | obj,
15 | key, {
16 | get: () =>
17 | getReducerKeyValue(simplerReduxStore, reducerKey, key),
18 | set: value =>
19 | setReducerKeyValue(simplerReduxStore, reducerKey, key, value)
20 | }
21 | )
22 | }
23 |
24 | const simulateProxy = (simplerReduxStore, reducerKey, initialState) =>
25 | Object.keys(initialState).reduce((obj, key) => {
26 | defineProxyGetSet(obj, simplerReduxStore, reducerKey, key)
27 | return obj
28 | }, {})
29 |
30 | const getProxyHandler = reducerKey => {
31 | return {
32 | get: (simplerReduxStore, key) =>
33 | getReducerKeyValue(simplerReduxStore, reducerKey, key),
34 | set: (simplerReduxStore, key, value) =>
35 | setReducerKeyValue(simplerReduxStore, reducerKey, key, value)
36 | }
37 | }
38 |
39 | export default (simplerReduxStore, reducerKey, initialState) => {
40 | if (proxyDefined()) {
41 | return new Proxy(simplerReduxStore, getProxyHandler(reducerKey))
42 | }
43 | return simulateProxy(simplerReduxStore, reducerKey, initialState)
44 | }
45 |
--------------------------------------------------------------------------------
/src/react-simpler-redux.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { connect } from 'react-redux'
3 | import PropTypes from 'prop-types'
4 | import hoistStatics from 'hoist-non-react-statics'
5 | import { stateAccessors, srOptions } from './simpler-redux'
6 | import { shallowSubObjectCompare, shallowCopy } from './util'
7 |
8 | // React lifecycle events supported in the model code.
9 | const reactLifeCycleEvents = {
10 | onConstructor: 'onConstructor',
11 | onRender: 'onRender',
12 | componentDidMount: 'componentDidMount',
13 | componentWillUnmount: 'componentWillUnmount',
14 | componentDidCatch: 'componentDidCatch',
15 | componentToRender: 'componentToRender'
16 | }
17 |
18 | //
19 | // Builds a mapDispatchToProps function based on the service functions and adds the store as
20 | // the first parameter to each service function and the rest of the parameters given
21 | // by the react UI component when called in react.
22 | //
23 | export const allServiceFunctionsToPropsWithStore = serviceFunctions => {
24 | const keys = Object.keys(serviceFunctions)
25 | return (_dispatch, ownProps) =>
26 | keys.reduce((obj, e) => {
27 | obj[e] = (...args) => serviceFunctions[e](ownProps.store, ...args)
28 | return obj
29 | }, {})
30 | }
31 |
32 | //
33 | // Builds a mapDispatchToProps function based on the service functions and adds
34 | // all of the parameters given by the react UI component when called in react.
35 | //
36 | export const allServiceFunctionsToProps = serviceFunctions =>
37 | () => ({ ...serviceFunctions })
38 |
39 | //
40 | // Builds a mapDispatchToProps function based on a serviceFunctionList object.
41 | // This allows including service functions from various modules.
42 | // The keylist allows selecting only a subset of a module's service functions.
43 | // If keylist is not specified then all service functions will be included.
44 | //
45 | export const allServiceFunctionsToPropsUsingServiceFunctionList = serviceFunctionList => {
46 | serviceFunctionList = [...serviceFunctionList]
47 | return (_dispatch, ownProps) =>
48 | serviceFunctionList.reduce((obj, e) => {
49 | const keylist = e.keylist ? e.keylist : Object.keys(e.serviceFunctions)
50 | keylist.forEach(key => {
51 | if (e.withStore) {
52 | obj[key] = (...args) => e.serviceFunctions[key](ownProps.store, ...args)
53 | } else {
54 | obj[key] = (...args) => e.serviceFunctions[key](...args)
55 | }
56 | })
57 | return obj
58 | }, {})
59 | }
60 |
61 | //
62 | // Builds a mapStateToProps function that returns the entire reducer state.
63 | //
64 | export const allStateToProps = reducerKey => {
65 | return state => {
66 | if (process.env.NODE_ENV !== 'production') {
67 | srOptions.mapStateToPropsCache({
68 | msg: `mapStateToProps fast calculation at ${reducerKey}, cache not needed.`,
69 | reducerKey: reducerKey
70 | })
71 | }
72 | return state[reducerKey]
73 | }
74 | }
75 |
76 | //
77 | // Builds a mapStateToProps function based on a selectors object.
78 | //
79 | export const allStateToPropsUsingSelectors = (selectors, reducerKey) => {
80 | const keys = Object.keys(selectors)
81 | if (!reducerKey) {
82 | return (
83 | state =>
84 | keys.reduce((obj, e) => {
85 | obj[e] = selectors[e](state)
86 | return obj
87 | }, {})
88 | )
89 | }
90 | // Implements a caching mechanism below such that if the state at the reducerKey did not change
91 | // then we return the previously calculated object.
92 | let prevState
93 | let lastresult
94 | return state => {
95 | if (prevState !== state[reducerKey]) {
96 | if (process.env.NODE_ENV !== 'production') {
97 | srOptions.mapStateToPropsCalc({
98 | msg: `selector calculated ${reducerKey}`,
99 | prevState,
100 | nextState: state[reducerKey],
101 | reducerKey
102 | })
103 | }
104 | prevState = state[reducerKey]
105 | lastresult = keys.reduce((obj, e) => {
106 | obj[e] = selectors[e](state)
107 | return obj
108 | }, {})
109 | } else {
110 | if (process.env.NODE_ENV !== 'production') {
111 | srOptions.mapStateToPropsCache({
112 | msg: `selector cache ${reducerKey}`,
113 | prevState,
114 | nextState: state[reducerKey],
115 | reducerKey
116 | })
117 | }
118 | }
119 | return lastresult
120 | }
121 | }
122 |
123 | //
124 | // Builds a mapStateToProps function based on a selectorList object.
125 | // This allows including selectors from various modules.
126 | // The keylist allows selecting only a subset of a module's selectors.
127 | // If keylist is not specified then all selectors will be included.
128 | //
129 | export const allStateToPropsUsingSelectorList = (selectorList, componentName) => {
130 | selectorList = [...selectorList]
131 | let useCache = true
132 | let reducerKeys = []
133 | for (let i = 0; i < selectorList.length; ++i) {
134 | if (selectorList[i].keylist === undefined) {
135 | selectorList[i].keylist = Object.keys(selectorList[i].selectors)
136 | }
137 | const reducerKey = selectorList[i].reducerKey
138 | if (reducerKey) {
139 | reducerKeys.push(reducerKey)
140 | } else {
141 | useCache = false
142 | }
143 | }
144 | if (componentName === undefined) {
145 | componentName = reducerKeys.toString()
146 | }
147 | if (useCache) {
148 | let prevStates = {}
149 | let lastResult
150 | return state => {
151 | if (!shallowSubObjectCompare(state, prevStates, reducerKeys)) {
152 | if (process.env.NODE_ENV !== 'production') {
153 | srOptions.mapStateToPropsCalc({
154 | msg: `Selector List calculated ${componentName}.`,
155 | nextState: shallowCopy(state, reducerKeys),
156 | prevState: prevStates,
157 | reducerKeys
158 | })
159 | }
160 | prevStates = shallowCopy(state, reducerKeys)
161 | lastResult = selectorList.reduce((obj, e) => {
162 | e.keylist.forEach(key => {
163 | obj[key] = e.selectors[key](state)
164 | })
165 | return obj
166 | }, {})
167 | } else {
168 | if (process.env.NODE_ENV !== 'production') {
169 | srOptions.mapStateToPropsCache({
170 | msg: `Selector List cache ${componentName}.`,
171 | nextState: shallowCopy(state, reducerKeys),
172 | prevState: prevStates,
173 | reducerKeys
174 | })
175 | }
176 | }
177 | return lastResult
178 | }
179 | }
180 | reducerKeys = []
181 | return state =>
182 | selectorList.reduce((obj, e) => {
183 | e.keylist.forEach(key => {
184 | obj[key] = e.selectors[key](state)
185 | })
186 | return obj
187 | }, {})
188 | }
189 |
190 | //
191 | // Builds a model selectors object from either initialUIState or initialState.
192 | // initialUIState should only contain keys that you want in the
193 | // props of the react component.
194 | // This is not for specialized selectors for the UI that require conjunctions or
195 | // selectors from other modules, etc.
196 | // It is only for simple selectors of the nature state => state[reducerKey][stateKey]
197 | //
198 | export const buildSelectorsFromUIState = (reducerKey, initialState) => {
199 | const keys = Object.keys(initialState)
200 | return keys.reduce((obj, e) => {
201 | obj[e] = state => state[reducerKey][e]
202 | return obj
203 | }, {})
204 | }
205 |
206 | //
207 | // Builds a mapStateToProps function that returns key/UI State pairs
208 | //
209 | const allStateToPropsUsingUIState = (reducerKey, initialUIState) => {
210 | const keys = Object.keys(initialUIState)
211 | let prevState
212 | let lastResult
213 | return state => {
214 | if (state[reducerKey] !== prevState) {
215 | if (process.env.NODE_ENV !== 'production') {
216 | srOptions.mapStateToPropsCalc({
217 | msg: `State props calculated at ${reducerKey}.`,
218 | nextState: state[reducerKey],
219 | prevState: prevState,
220 | reducerKey
221 | })
222 | }
223 | prevState = state[reducerKey]
224 | lastResult = shallowCopy(state[reducerKey], keys)
225 | } else {
226 | if (process.env.NODE_ENV !== 'production') {
227 | srOptions.mapStateToPropsCache({
228 | msg: `State props cache at ${reducerKey}.`,
229 | nextState: state[reducerKey],
230 | prevState: prevState,
231 | reducerKey
232 | })
233 | }
234 | }
235 | return lastResult
236 | }
237 | }
238 |
239 | const buildCachedMapStateToProps = (mapStateToProps, mapStateToPropsReducerKeys, componentName) => {
240 | if (!Array.isArray(mapStateToPropsReducerKeys)) {
241 | mapStateToPropsReducerKeys = [mapStateToPropsReducerKeys]
242 | }
243 | if (componentName === undefined) {
244 | componentName = mapStateToPropsReducerKeys.toString()
245 | }
246 | let prevStates = {}
247 | let lastResult
248 | return state => {
249 | if (!shallowSubObjectCompare(state, prevStates, mapStateToPropsReducerKeys)) {
250 | if (process.env.NODE_ENV !== 'production') {
251 | srOptions.mapStateToPropsCalc({
252 | msg: `mapStateToProps calculated ${componentName}.`,
253 | nextState: shallowCopy(state, mapStateToPropsReducerKeys),
254 | prevState: prevStates,
255 | reducerKeys: mapStateToPropsReducerKeys
256 | })
257 | }
258 | prevStates = shallowCopy(state, mapStateToPropsReducerKeys)
259 | lastResult = mapStateToProps(state)
260 | } else {
261 | if (process.env.NODE_ENV !== 'production') {
262 | srOptions.mapStateToPropsCache({
263 | msg: `mapStateToProps cache ${componentName}.`,
264 | nextState: shallowCopy(state, mapStateToPropsReducerKeys),
265 | prevState: prevStates,
266 | reducerKeys: mapStateToPropsReducerKeys
267 | })
268 | }
269 | }
270 | return lastResult
271 | }
272 | }
273 |
274 | const connectWithStoreBase = (
275 | options
276 | ) => {
277 | let {
278 | uiComponent,
279 | reduxOptions,
280 | storeIsDefinedCallback,
281 | reducerKey,
282 | isDynamicReducer,
283 | initialState,
284 | mapStateToProps,
285 | mapDispatchToProps,
286 | serviceFunctions,
287 | serviceFunctionList,
288 | selectors,
289 | selectorList,
290 | initialUIState,
291 | noStoreParameterOnServiceFunctions,
292 | mergeProps,
293 | mapStateToPropsReducerKeys,
294 | componentName
295 | } = options
296 |
297 | if (process.env.NODE_ENV !== 'production') {
298 | if (uiComponent === undefined) {
299 | throw new Error(`connectWithStore: options.uiComponent cannot be undefined, reducerKey=${reducerKey}.`)
300 | }
301 | if (storeIsDefinedCallback && typeof storeIsDefinedCallback !== 'function') {
302 | throw new Error(`connectWithStore: options.storeIsDefinedCallback must be a function, reducerKey=${reducerKey}.`)
303 | }
304 | if (selectors !== undefined && initialUIState !== undefined) {
305 | throw new Error(`connectWithStore: Cannot export both selectors and initialUIState, reducerKey=${reducerKey}.`)
306 | }
307 | let displayName = ''
308 | if (uiComponent !== undefined) {
309 | if (uiComponent.displayName !== undefined) {
310 | displayName = uiComponent.displayName
311 | } else {
312 | displayName = uiComponent.name
313 | }
314 | }
315 | if (selectorList !== undefined) {
316 | selectorList.forEach(e => {
317 | if (e.keylist !== undefined) {
318 | e.keylist.forEach(key => {
319 | if (typeof e.selectors[key] !== 'function') {
320 | throw new Error(`connectWithStore ${displayName}: The selectors key ${key} is not in the selectors ${e.keylist.toString()}.`)
321 | }
322 | })
323 | }
324 | })
325 | }
326 | if (serviceFunctionList !== undefined) {
327 | serviceFunctionList.forEach(e => {
328 | if (e.keylist !== undefined) {
329 | e.keylist.forEach(key => {
330 | if (typeof e.serviceFunctions[key] !== 'function') {
331 | throw new Error(`connectWithStore ${displayName}: The serviceFunctions key ${key} is not in the serviceFunctionList ${e.keylist.toString()}.`)
332 | }
333 | })
334 | }
335 | })
336 | }
337 | }
338 |
339 | if (componentName === undefined) {
340 | componentName = reducerKey
341 | }
342 |
343 | // Default initialState (reducer state) to initialUIState (component props state).
344 | if (initialState === undefined) {
345 | initialState = initialUIState
346 | }
347 |
348 | // Default initialUIState (component props state) to initialState (reducer state).
349 | if (initialUIState === undefined) {
350 | initialUIState = initialState
351 | }
352 |
353 | const withRef = reduxOptions && reduxOptions.withRef
354 | if (initialState !== undefined) {
355 | initialState = { ...initialState }
356 | }
357 |
358 | // If mapStateToProps is defined by the consumer then keep it no matter what.
359 | if (mapStateToProps !== undefined) {
360 | if (mapStateToPropsReducerKeys !== undefined) {
361 | mapStateToProps = buildCachedMapStateToProps(mapStateToProps, mapStateToPropsReducerKeys, componentName)
362 | }
363 | } else {
364 | if (selectorList !== undefined) {
365 | mapStateToProps = allStateToPropsUsingSelectorList(selectorList, componentName)
366 | } else if (selectors !== undefined) {
367 | mapStateToProps = allStateToPropsUsingSelectors(selectors, reducerKey)
368 | } else if (reducerKey !== undefined && initialUIState !== undefined) {
369 | // This is for efficiency. initialUIState and initialState are the same so
370 | // mapStateToProps simply returns the entire reducerKey state.
371 | if (Object.keys(initialUIState).length === Object.keys(initialState).length) {
372 | mapStateToProps = allStateToProps(reducerKey)
373 | } else {
374 | mapStateToProps = allStateToPropsUsingUIState(reducerKey, initialUIState)
375 | }
376 | }
377 | }
378 |
379 | // If mapDispatchToProps is defined by the consumer then keep it no matter what.
380 | if (mapDispatchToProps === undefined) {
381 | if (serviceFunctionList !== undefined) {
382 | mapDispatchToProps = allServiceFunctionsToPropsUsingServiceFunctionList(serviceFunctionList)
383 | } else if (serviceFunctions !== undefined) {
384 | if (noStoreParameterOnServiceFunctions) {
385 | mapDispatchToProps = allServiceFunctionsToProps(serviceFunctions)
386 | } else {
387 | mapDispatchToProps = allServiceFunctionsToPropsWithStore(serviceFunctions)
388 | }
389 | }
390 | }
391 |
392 | // Call the react-redux connect.
393 | const ConnectedComponent = connect(
394 | mapStateToProps,
395 | mapDispatchToProps,
396 | mergeProps,
397 | reduxOptions
398 | )(uiComponent)
399 |
400 | class HOC extends React.Component {
401 | constructor (props, context) {
402 | super(props, context)
403 | // Handles the dynamic loading of the reducer.
404 | if (isDynamicReducer !== false && this.context.store.isDynamicReducerLoading()) {
405 | // This will build the reducer and add it to the reducers object.
406 | if (reducerKey !== undefined && initialState !== undefined) {
407 | this.context.store.addReducer(reducerKey, initialState)
408 | }
409 | }
410 | // Handles a callback for the consumer to cache and/or use the store.
411 | if (storeIsDefinedCallback) {
412 | storeIsDefinedCallback(this.context.store, stateAccessors)
413 | }
414 | this.setWrappedInstance = this.setWrappedInstance.bind(this)
415 | }
416 | // Support the redux connect getWrappedInstance out of this HOC.
417 | // This way, the consumer does nothing different when using this HOC
418 | // vs redux connected components when handling refs.
419 | setWrappedInstance (ref) {
420 | if (!withRef) {
421 | return
422 | }
423 | // Refer to the original instance of the component wrapped with connect.
424 | if (ref) {
425 | if (process.env.NODE_ENV !== 'production') {
426 | if (typeof ref.getWrappedInstance !== 'function') {
427 | console.log('There is something wrong with redux connect.')
428 | return
429 | }
430 | }
431 | this.wrappedInstance = ref.getWrappedInstance()
432 | }
433 | }
434 | getWrappedInstance () {
435 | if (process.env.NODE_ENV !== 'production') {
436 | if (this.wrappedInstance === undefined) {
437 | console.log('The getWrappedInstance return is undefined. Did you use the withRef: true option?')
438 | }
439 | }
440 | return this.wrappedInstance
441 | }
442 | render () {
443 | // Add the store to the props of the redux connected component so that it can be referenced
444 | // in mapDispatchToProps with ownProps.
445 | return (
446 |
451 | )
452 | }
453 | }
454 |
455 | HOC.displayName = `connectWithStore(${ConnectedComponent.displayName || ConnectedComponent.name})`
456 | // Opt in for the context.
457 | HOC.contextTypes = {
458 | store: PropTypes.object
459 | }
460 | return hoistStatics(HOC, ConnectedComponent)
461 | }
462 |
463 | //
464 | // This supports moving the react life cycle events into the model/business code serviceFunctions functions object.
465 | //
466 | class ReactLifeCycleComponent extends React.Component {
467 | constructor (props) {
468 | super(props)
469 | this.runFunction = this.runFunction.bind(this)
470 | this.runFunction(this.props.onConstructor)
471 | }
472 | runFunction (func, args = []) {
473 | return func ? func.call(this, ...args) : null
474 | }
475 | componentDidMount () {
476 | this.runFunction(this.props.componentDidMount)
477 | }
478 | componentWillUnmount () {
479 | this.runFunction(this.props.componentWillUnmount)
480 | }
481 | componentDidCatch (...args) {
482 | this.runFunction(this.props.componentDidCatch, args)
483 | }
484 | render () {
485 | this.runFunction(this.props.onRender)
486 | // Render prop
487 | return this.props.componentToRender()
488 | }
489 | }
490 |
491 | ReactLifeCycleComponent.propTypes = {
492 | onConstructor: PropTypes.func,
493 | onRender: PropTypes.func,
494 | componentDidMount: PropTypes.func,
495 | componentWillUnmount: PropTypes.func,
496 | componentDidCatch: PropTypes.func,
497 | componentToRender: PropTypes.func.isRequired
498 | }
499 |
500 | export const hookedLifeCycleComponent = (Component, props = {}) => {
501 | const {
502 | onConstructor,
503 | onRender,
504 | componentDidMount,
505 | componentWillUnmount,
506 | componentDidCatch,
507 | componentToRender,
508 | ...propsToPass
509 | } = props
510 | return (
511 | }
518 | />
519 | )
520 | }
521 |
522 | export const connectLifeCycleComponentWithStore = options => {
523 | if (process.env.NODE_ENV !== 'production') {
524 | if (options.serviceFunctions === undefined) {
525 | throw new Error('connectLifeCycleComponentWithStore: You must define and export a serviceFunctions object in the model code in order to use this function.')
526 | }
527 | }
528 | const component = options.uiComponent
529 | const hookedLCC = props => hookedLifeCycleComponent(component, props)
530 | const newOptions = { ...options, uiComponent: hookedLCC }
531 | return connectWithStoreBase(newOptions)
532 | }
533 |
534 | /*
535 | options object parameter
536 |
537 | reducerKey - (required) The key in the redux store for this module
538 | initialState - (required) The initial state that will be used in the reducer for initialization.
539 | initialUIState - (optional) If this is specified then simpler-redux will build a mapStateToProps
540 | function based on the keys in this object.
541 | selectors - (optional) If this is specified then simpler-redux will build a mapStateToProps
542 | function based on the selectors object.
543 | selectorList - (Optionsl) An array of {selectors, keylist[list of selector keys]}. This allows
544 | combining selectors from different modules into one in order to build a mapStateToProps that
545 | includes key/values from other reducers keys including the component reducer selectors. If you
546 | specify keylist then you can include only a subset of the selectors indtead of all of them.
547 | serviceFunctions - (optional) If this is specified then simpler-redux will build a mapDispatchToProps
548 | function based on the keys in this object. These will be the service functions exposed to the
549 | the react component in the props.
550 | serviceFunctionList` - An array of {serviceFunctions, keylist[list of serviceFunctions keys],
551 | withStore}. This allows combining serviceFunctions from different modules into one in order
552 | to build a mapDispatchToProps that includes key/values from other module serviceFunctions.
553 | The keylist allows you to select only a subset of the associated service functions. The withStore
554 | set to true will cause the store to be the first parameter for all the service functions when
555 | called with the UI parameters following after.
556 | noStoreParameterOnServiceFunctions = true (Optional) - By default, simpler-redux injects the store as the
557 | first parameter when any service function is called by the UI. The UI parameters follow.
558 | If this is set to true then simpler-redux will not do this store injection.
559 | storeIsDefinedCallback(store, stateAccessors) - (Optional) If this is specified then simpler-redux will call
560 | this function with the simpler redux store as a parameter when the store becomes available to the react
561 | component. Use this to call the simpler-redux stateAccessors in order to gain access to
562 | setState, getState and reducerState.
563 | Example:
564 | let setState, reducerState
565 | export const storeIsDefinedCallback = (store, stateAccessors) =>
566 | ({setState, reducerState} = stateAccessors(store, reducerKey, initialState))
567 | isDynamicReducer - (Optional) This supports dynamic reducer loading. For this, simpler-redux
568 | automatically takes care of building the reducer and loading it into the reducers object.
569 |
570 | Note: If you present any redux state in the react component then you must define and export either
571 | a selectors object or an initialUIState object. Otherwise, you will not have any state in
572 | the props of the react component.
573 | */
574 | export const connectWithStore = options => {
575 | // First decide if the serviceFunctions object contains react lifecycle calls.
576 | if (options.serviceFunctions !== undefined) {
577 | const hasLifeCycle = Object.keys(options.serviceFunctions).some(e =>
578 | reactLifeCycleEvents[e] !== undefined
579 | )
580 | if (hasLifeCycle) {
581 | return connectLifeCycleComponentWithStore(options)
582 | }
583 | }
584 | // No react lifecycle calls.
585 | return connectWithStoreBase(options)
586 | }
587 |
--------------------------------------------------------------------------------
/src/simpler-redux.js:
--------------------------------------------------------------------------------
1 | /*
2 | Written by Andrew Banks. MIT license.
3 | */
4 | import { createStore as createReduxStore, combineReducers } from 'redux'
5 | import { defaultOptions } from './util'
6 | import getReducerKeyProxy from './proxy'
7 |
8 | const simplerReduxReducerKey = '@@@@@srReducerKey'
9 | const simplerReduxObjectToMergeKey = '@@@@@srObjectToMergeKey'
10 |
11 | const objectType = obj => Object.prototype.toString.call(obj).slice(8, -1)
12 | const isObjectType = obj => objectType(obj) === 'Object'
13 |
14 | export let srOptions
15 |
16 | const makeSetRState = reduxStore => {
17 | return (reducerKey, objToMerge, type) => {
18 | if (type === undefined) {
19 | type = reducerKey
20 | }
21 | if (process.env.NODE_ENV !== 'production') {
22 | if (typeof reducerKey !== 'string') {
23 | throw new Error('setRState: The first argument must be a string.')
24 | }
25 | if (!isObjectType(objToMerge)) {
26 | throw new Error('setRState: The second argument must be a primitive object type.')
27 | }
28 | if (typeof type !== 'string') {
29 | throw new Error('setRState: The third argument must be a string.')
30 | }
31 | }
32 |
33 | reduxStore.dispatch({
34 | [simplerReduxReducerKey]: reducerKey,
35 | [simplerReduxObjectToMergeKey]: objToMerge,
36 | type
37 | })
38 |
39 | reduxStore.listeners.forEach(listenerObj => {
40 | listenerObj.listener(reducerKey, objToMerge, type)
41 | })
42 | }
43 | }
44 |
45 | const makeGetRState = reduxStore => {
46 | return reducerKey => {
47 | const state = reduxStore.getState()[reducerKey]
48 | if (process.env.NODE_ENV !== 'production') {
49 | if (typeof reducerKey !== 'string') {
50 | throw new Error('getRState: The first argument must be a string.')
51 | }
52 | if (state === undefined) {
53 | throw new Error(`The reducerKey state at ${reducerKey} is undefined. Did you forget to export an initialUIState or initialState from your model code?`)
54 | }
55 | }
56 | return state
57 | }
58 | }
59 |
60 | // These listeners do what redux subscribers should have done. It gives the reducerKey being modified
61 | // so that a listener can quickly decide if it is concerned about changes in that reducerKey.
62 | const addListener = store => {
63 | return listener => {
64 | if (process.env.NODE_ENV !== 'production') {
65 | if (typeof listener !== 'function') {
66 | throw new Error('addListener: The first argument must be a function.')
67 | }
68 | }
69 | const id = store.listenerId++
70 | store.listeners.push({ listener, id })
71 | // Return a function that will remove this listener.
72 | return () => {
73 | let i = 0
74 | for (; i < store.listeners.length && store.listeners[i].id !== id; ++i);
75 | if (i < store.listeners.length) {
76 | store.listeners.splice(i, 1)
77 | }
78 | }
79 | }
80 | }
81 |
82 | //
83 | // Call this to generate your reducer.
84 | //
85 | export const generalReducer = (reducerKey, initialState) => {
86 | if (process.env.NODE_ENV !== 'production') {
87 | if (reducerKey === undefined) {
88 | throw new Error('generalReducer: reducerKey must be defined.')
89 | }
90 | if (initialState === undefined) {
91 | throw new Error('generalReducer: initialState must be defined.')
92 | }
93 | }
94 | initialState = { ...initialState }
95 | return (state = initialState, action) => {
96 | if (action[simplerReduxReducerKey] === reducerKey) {
97 | return { ...state, ...action[simplerReduxObjectToMergeKey] }
98 | }
99 | return state
100 | }
101 | }
102 |
103 | // Allows dynamic loading of simpler redux mvc components and their associated reducers.
104 | const buildAddReducer = (store, preloadedState) => {
105 | return (reducerKey, initialState) => {
106 | if (process.env.NODE_ENV !== 'production') {
107 | if (reducerKey === undefined) {
108 | throw new Error('addReducer: The first argument (reducerKey) must be defined.')
109 | }
110 | if (initialState === undefined) {
111 | throw new Error('addReducer: The second argument (initialState) must be defined.')
112 | }
113 | }
114 | // First load the initial state for the reducer. This is the developer defined initial state.
115 | let state = { ...initialState }
116 | // Then load the preloadedState for the keys that exist in the initial state.
117 | // Therefore, keys in preloadedState[reducerKey] that are not part of the current
118 | // state shape in initialState are considered deprecated and are ignored. Otherwise,
119 | // redux tosses a warning flag.
120 | const preloadedStateAtReducerKey = preloadedState[reducerKey]
121 | if (preloadedStateAtReducerKey !== undefined) {
122 | Object.keys(preloadedStateAtReducerKey).forEach(key => {
123 | if (state[key] !== undefined) {
124 | state[key] = preloadedStateAtReducerKey[key]
125 | }
126 | })
127 | }
128 | // One reducer with no typical redux reducers.
129 | if (store.isOneReducer) {
130 | let currentWholeState = store.getState()
131 | // Do not set initialState on HMR.
132 | if (currentWholeState === undefined || currentWholeState[reducerKey] === undefined) {
133 | // Set the initialState state at the reducerKey.
134 | store.setRState(reducerKey, state)
135 | }
136 | return
137 | }
138 | // Generate the reducer for reducerKey.
139 | const reducer = generalReducer(reducerKey, state)
140 | // Add the reducer to the current list.
141 | store.currentReducersObject = { ...store.currentReducersObject, [reducerKey]: reducer }
142 | // Replace the redux reducers with the new list.
143 | store.replaceReducer(combineReducers(store.currentReducersObject))
144 | }
145 | }
146 |
147 | //
148 | // This must be called with the redux store as a parameter after a createStore.
149 | // Then use the return of this function in the react-redux Provider element as the store.
150 | // If you pass in a rootReducersObject then you can use simpler-redux dynamic loading of reducers.
151 | // Note: rootReducersObject is the actual reducers object and not the combineReducers output.
152 | // If you want only dynamic reducers, use state => state (null reducer) for the redux createStore reducer
153 | // and { } as the rootReducersObject for the call below.
154 | // If you use rootReducersObject then you should also pass in preloadedState (if it exists).
155 | // options
156 | // 1) isDynamicReducer - Default to dynamic reducer loading for react components so that you do not have to specify isDynamicReducer in each component module.
157 | //
158 | export const registerSimplerRedux = (
159 | reduxStore,
160 | rootReducersObject,
161 | preloadedState = {},
162 | options = {}
163 | ) => {
164 | srOptions = defaultOptions(options)
165 | let wrappedReduxStore = Object.create(reduxStore)
166 | wrappedReduxStore.setRState = makeSetRState(wrappedReduxStore)
167 | wrappedReduxStore.getRState = makeGetRState(wrappedReduxStore)
168 | wrappedReduxStore.addListener = addListener(wrappedReduxStore)
169 | wrappedReduxStore.listenerId = 0
170 | wrappedReduxStore.listeners = []
171 | // Support for dynamic reducer loading.
172 | if (rootReducersObject !== undefined) {
173 | wrappedReduxStore.isDynamicReducerLoading = () => true
174 | wrappedReduxStore.currentReducersObject = { ...rootReducersObject }
175 | wrappedReduxStore.addReducer = buildAddReducer(wrappedReduxStore, { ...preloadedState })
176 | } else {
177 | wrappedReduxStore.isDynamicReducerLoading = () => false
178 | if (process.env.NODE_ENV !== 'production') {
179 | wrappedReduxStore.addReducer = () => {
180 | throw new Error('To call addReducer, you must specify a rootReducersObject in the 2nd argument of registerSimplerRedux which can be just {}.')
181 | }
182 | }
183 | }
184 | return wrappedReduxStore
185 | }
186 |
187 | //
188 | // One reducer is not compatible with existing redux code. Must be all simpler-redux.
189 | // This is the only reducer called for all state transitions.
190 | //
191 | const oneReducer = (state = {}, action) => {
192 | const reducerKey = action[simplerReduxReducerKey]
193 | const objToMerge = action[simplerReduxObjectToMergeKey]
194 | // This is some redux thing, not from our setRState.
195 | if (reducerKey === undefined) {
196 | return state
197 | }
198 | // Must change the upper level redux state pointer or redux does not recognize a state change.
199 | state = { ...state }
200 | // Merge the incoming reducerKey state at the reducerKey
201 | state[reducerKey] = {...state[reducerKey], ...objToMerge}
202 | return state
203 | }
204 |
205 | // This cannot be used with redux reducers.
206 | export const createStore = (preloadedState, enhancer, options = {}) => {
207 | const reduxStore = createReduxStore(
208 | oneReducer,
209 | preloadedState,
210 | enhancer
211 | )
212 | const wrappedReduxStore = registerSimplerRedux(
213 | reduxStore,
214 | {},
215 | preloadedState,
216 | options
217 | )
218 | wrappedReduxStore.isOneReducer = true
219 | return wrappedReduxStore
220 | }
221 |
222 | // This makes it easier to access reducerKey state previously given a reducerKey.
223 | export const getStateFunction = reducerKey =>
224 | (state, key) =>
225 | state[reducerKey][key]
226 |
227 | // This makes it easier to set reducerKey state previously given a reducerKey.
228 | export const setStateFunction = reducerKey =>
229 | (store, mergeState, type) =>
230 | store.setRState(reducerKey, mergeState, type)
231 |
232 | // Use this to generate shared module keys.
233 | export const makeSharedModuleKeyName = (key, options) =>
234 | `${key}${options.id}`
235 |
236 | // Sifts out dynamically loaded reducer keys from the preloaded state in order to avoid
237 | // a redux warning. Use the return of this function to pass into the redux createStore.
238 | export const reducersPreloadedState = (reducersObject, preloadedState) =>
239 | Object.keys(preloadedState).reduce((obj, key) => {
240 | if (reducersObject[key] !== undefined) {
241 | obj[key] = preloadedState[key]
242 | }
243 | return obj
244 | }, {})
245 |
246 | const getState = (store, reducerKey) =>
247 | () => store.getRState(reducerKey)
248 |
249 | const setState = (store, reducerKey) =>
250 | (mergeState, type) => {
251 | if (process.env.NODE_ENV !== 'production') {
252 | if (!isObjectType(mergeState)) {
253 | throw new Error('setState: The first argument must be a primitive object type.')
254 | }
255 | }
256 | store.setRState(reducerKey, mergeState, type)
257 | }
258 |
259 | //
260 | // Only call this in the storeIsDefinedCallback sent into connectWithStore above.
261 | // Use the store parameter provided in connectWithStore along with the reducerKey
262 | // in the module.
263 | //
264 | export const stateAccessors = (store, reducerKey, initialState) => {
265 | if (process.env.NODE_ENV !== 'production') {
266 | if (store === undefined) {
267 | throw new Error('The first parameter (store) to stateAccessors must be defined.')
268 | }
269 | if (typeof reducerKey !== 'string') {
270 | throw new Error('The second parameter (reducerKey) to stateAccessors must be a string.')
271 | }
272 | }
273 | let ret = {
274 | getState: getState(store, reducerKey),
275 | setState: setState(store, reducerKey)
276 | }
277 |
278 | if (initialState !== undefined) {
279 | ret.reducerState = getReducerKeyProxy(store, reducerKey, initialState)
280 | }
281 |
282 | return ret
283 | }
284 |
285 | // Creates general purpose module data under a redux reducerKey.
286 | // A redux store must be imported from your redux createStore module for the argument below.
287 | export const createModuleData = (store, reducerKey, initialState) => {
288 | if (process.env.NODE_ENV !== 'production') {
289 | if (!store.isDynamicReducerLoading()) {
290 | throw new Error('To call createModuleData, you must specify a rootReducersObject in the 2nd argument of registerSimplerRedux which can be just {}.')
291 | }
292 | if (store === undefined) {
293 | throw new Error('The first parameter (store) to createModuleData must be defined.')
294 | }
295 | if (typeof reducerKey !== 'string') {
296 | throw new Error('The seccond parameter (reducerKey) to createModuleData must a string.')
297 | }
298 | if (initialState === undefined) {
299 | throw new Error('The third parameter (initialState) to createModuleData must be defined.')
300 | }
301 | }
302 | store.addReducer(reducerKey, initialState)
303 | return stateAccessors(store, reducerKey, initialState)
304 | }
305 |
--------------------------------------------------------------------------------
/src/util.js:
--------------------------------------------------------------------------------
1 | let totalMSTPCalcs = 0
2 | let totalMSTPCache = 0
3 | let useDefaultMSTPCacheOnlyLogging = false
4 |
5 | const showMessage = msg =>
6 | console.log(msg)
7 |
8 | export const shallowSubObjectCompare = (obj, subObj, subObjectkeys) => {
9 | const len = subObjectkeys.length
10 | for (let i = 0; i < len; ++i) {
11 | const key = subObjectkeys[i]
12 | if (subObj[key] !== obj[key]) {
13 | return false
14 | }
15 | }
16 | return true
17 | }
18 |
19 | export const shallowCopy = (obj, copykeys) => {
20 | if (copykeys === undefined) {
21 | copykeys = Object.keys(obj)
22 | }
23 | let subObj = {}
24 | const len = copykeys.length
25 | for (let i = 0; i < len; ++i) {
26 | const key = copykeys[i]
27 | subObj[key] = obj[key]
28 | }
29 | return subObj
30 | }
31 |
32 | const displayMessage = obj => {
33 | if (!obj.prevState) {
34 | showMessage(obj.msg)
35 | } else {
36 | showMessage(`${obj.msg}
37 | prevState: %onextState: %o`,
38 | obj.prevState,
39 | obj.nextState
40 | )
41 | }
42 |
43 | // Give the stats as performance feedback to developers.
44 | showMessage(`totalMSTPCalcs=${totalMSTPCalcs}, totalMSTPCache=${totalMSTPCache}`)
45 | }
46 |
47 | const mapStateToPropsCache = obj => {
48 | totalMSTPCache++
49 | if (useDefaultMSTPCacheOnlyLogging) {
50 | showMessage(`totalMSTPCache=${totalMSTPCache}`)
51 | } else {
52 | displayMessage(obj)
53 | }
54 | }
55 |
56 | const mapStateToPropsCalc = obj => {
57 | totalMSTPCalcs++
58 | if (useDefaultMSTPCacheOnlyLogging) {
59 | showMessage(`totalMSTPCalcs=${totalMSTPCalcs}`)
60 | } else {
61 | displayMessage(obj)
62 | }
63 | }
64 |
65 | export const defaultOptions = options => {
66 | if (options === undefined) {
67 | return
68 | }
69 | options = { ...options }
70 | useDefaultMSTPCacheOnlyLogging = options.useDefaultMSTPCacheOnlyLogging
71 | if (!options.mapStateToPropsCache) {
72 | options.mapStateToPropsCache = () => { }
73 | }
74 | if (!options.mapStateToPropsCalc) {
75 | options.mapStateToPropsCalc = () => { }
76 | }
77 | if (options.useDefaultMSTPCacheLogging || options.useDefaultMSTPCacheOnlyLogging) {
78 | options.mapStateToPropsCalc = mapStateToPropsCalc
79 | options.mapStateToPropsCache = mapStateToPropsCache
80 | }
81 | return options
82 | }
83 |
--------------------------------------------------------------------------------
/test/App.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Provider } from 'react-redux'
3 | import store from './reduxstore'
4 | import Counter from './Counter'
5 | import Counter2 from './Counter2'
6 | import Counter3 from './Counter3'
7 | import Counter4 from './Counter4'
8 | import Counter5 from './Counter5'
9 | import Counter6 from './Counter6'
10 | import Counter7 from './Counter7'
11 | import Counter8 from './Counter8'
12 | import WrapCounter1 from './WrapCounter1'
13 |
14 | export default () =>
15 |
16 |