Skip to content

Commit 43f9400

Browse files
committed
Initial commit
0 parents  commit 43f9400

21 files changed

+4736
-0
lines changed

.babelrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"presets": ["es2015", "stage-2", "react"],
3+
"plugins": [
4+
"transform-export-extensions"
5+
],
6+
}

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dist/
2+
coverage/

.eslintrc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"parser": "babel-eslint",
3+
"extends": [
4+
"airbnb-base",
5+
"eslint:recommended",
6+
"plugin:react/recommended"
7+
],
8+
"plugins": [
9+
"react"
10+
],
11+
"rules": {
12+
"indent": ["error", 4],
13+
"arrow-body-style": 0,
14+
"react/jsx-uses-vars": ["error"],
15+
"react/react-in-jsx-scope": ["error"]
16+
},
17+
"env": {
18+
"jest": true
19+
}
20+
}

.gitignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
8+
# Coverage directory used by tools like istanbul
9+
coverage
10+
11+
# Dependency directories
12+
node_modules/
13+
14+
# Optional npm cache directory
15+
.npm
16+
17+
# Optional eslint cache
18+
.eslintcache
19+
20+
# Optional REPL history
21+
.node_repl_history
22+
23+
# Output of 'npm pack'
24+
*.tgz
25+
26+
# Yarn Integrity file
27+
.yarn-integrity
28+
29+
# dotenv environment variables file
30+
.env

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Jason Morita
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.MD

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# react-redux-ui-state
2+
React higher order component to provide UI state for components.
3+
4+
## Problems this is trying reduce
5+
- Reduce the need to reach for `setState` to save time for simple flags
6+
- Reduce reducer boilerplate for properties like UI flags
7+
- Reduce tediousness of instances of the same component needing the same flags
8+
9+
## How it works
10+
- Create a config object with an optional name property and a function (which gets passed props) which returns a state object consisting of your initial state.
11+
- Wrap your component in the HOC.
12+
- An action gets dispatched like `UI_STATE_ADD:some-component_<uuid>`
13+
- The initial state gets added to a `uiState` slice of the store with the name/state.
14+
- Call `setUiState` with a new state object.
15+
- An action gets dispatched like `UI_STATE_SET:some-component_<uuid>`
16+
17+
Simple update function, same as React's `setState`.
18+
19+
Simple to use:
20+
21+
```javascript
22+
// rootReducer.js
23+
import { combineReducers } from 'redux';
24+
import { uiStateReducer } from 'react-redux-ui-state';
25+
26+
export default function createRootReducer (reducers) {
27+
return combineReducers({
28+
...reducers,
29+
uiState: uiStateReducer,
30+
});
31+
}
32+
33+
// someComponent.js
34+
import React from 'react';
35+
import { uiState } from 'react-redux-ui-state';
36+
37+
export function someComponent ({
38+
someUiFlag,
39+
setUiState,
40+
uiStateName,
41+
}) {
42+
return (
43+
<button onClick={() => setUiState({ someUiFlag: !someUiFlag })}>
44+
Toggle flag for {uiStateName}
45+
</button>
46+
);
47+
}
48+
49+
const uiStateConfig = {
50+
name: 'some-component',
51+
state: () => ({
52+
someUiFlag: false,
53+
}),
54+
};
55+
56+
export default uiState(uiStateConfig)(someComponent);
57+
```
58+
59+
This is a work in progress. There could well be performance issues when used "at scale". Feel free to open a PR.

dist/hoc.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
'use strict';
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
7+
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; };
8+
9+
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; }; }();
10+
11+
var _react = require('react');
12+
13+
var _react2 = _interopRequireDefault(_react);
14+
15+
var _propTypes = require('prop-types');
16+
17+
var _propTypes2 = _interopRequireDefault(_propTypes);
18+
19+
var _reactRedux = require('react-redux');
20+
21+
var _ = require('./');
22+
23+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
24+
25+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
26+
27+
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; }
28+
29+
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; }
30+
31+
exports.default = function (config) {
32+
return function (WrappedComponent) {
33+
var uiStateName = (0, _.generateName)(config.name);
34+
var maps = (0, _.generateMaps)(uiStateName);
35+
36+
var uiState = function (_Component) {
37+
_inherits(uiState, _Component);
38+
39+
function uiState(props) {
40+
_classCallCheck(this, uiState);
41+
42+
var _this = _possibleConstructorReturn(this, (uiState.__proto__ || Object.getPrototypeOf(uiState)).call(this, props));
43+
44+
_this.state = config.state(_this.props);
45+
console.log('this.props', _this.props); // eslint-disable-line
46+
return _this;
47+
}
48+
49+
// this is essentially a "run-once"
50+
51+
52+
_createClass(uiState, [{
53+
key: 'componentWillMount',
54+
value: function componentWillMount() {
55+
this.props.add(this.state);
56+
}
57+
}, {
58+
key: 'render',
59+
value: function render() {
60+
var _this2 = this;
61+
62+
var setUiState = function setUiState(state, cb) {
63+
// we are using setState internally to take advantage of React
64+
return _this2.setState(state, function () {
65+
_this2.props.set(state);
66+
67+
// optional callback to match setState API
68+
if (cb) {
69+
cb();
70+
}
71+
});
72+
};
73+
74+
// these get passed to the child as props
75+
var uiStateProps = {
76+
setUiState: setUiState,
77+
setUIState: setUiState, // avoid case-sensitive typos
78+
uiStateName: uiStateName // the generated name for this component's state slice
79+
};
80+
81+
// wrapped component with its props, the state from HOC and uiStateProps
82+
return _react2.default.createElement(WrappedComponent, _extends({}, this.props, this.state, uiStateProps));
83+
}
84+
}]);
85+
86+
return uiState;
87+
}(_react.Component);
88+
89+
uiState.propTypes = {
90+
add: _propTypes2.default.func.isRequired,
91+
set: _propTypes2.default.func.isRequired
92+
};
93+
94+
// the HOC itself is wrapped in connect
95+
return (0, _reactRedux.connect)(maps.stateToProps, maps.dispatchToProps)(uiState);
96+
};
97+
};

dist/index.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use strict';
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
exports.types = exports.uiState = exports.uiStateReducer = undefined;
7+
exports.generateName = generateName;
8+
exports.generateType = generateType;
9+
exports.generateMaps = generateMaps;
10+
11+
var _v = require('uuid/v4');
12+
13+
var _v2 = _interopRequireDefault(_v);
14+
15+
var _reducer = require('./reducer');
16+
17+
var _reducer2 = _interopRequireDefault(_reducer);
18+
19+
var _hoc = require('./hoc');
20+
21+
var _hoc2 = _interopRequireDefault(_hoc);
22+
23+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
24+
25+
exports.uiStateReducer = _reducer2.default;
26+
exports.uiState = _hoc2.default;
27+
var types = exports.types = {
28+
add: 'UI_STATE_ADD',
29+
set: 'UI_STATE_SET'
30+
};
31+
32+
function generateName() {
33+
var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
34+
35+
return (name || 'component') + '_' + (0, _v2.default)();
36+
}
37+
38+
function generateType(type, name) {
39+
return type + ':' + name;
40+
}
41+
42+
// these are set as props on the HOC
43+
function generateMaps(name) {
44+
return {
45+
dispatchToProps: function dispatchToProps(dispatch) {
46+
return {
47+
add: function add() {
48+
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
49+
50+
return dispatch({
51+
type: generateType(types.add, name),
52+
payload: {
53+
name: name,
54+
state: state
55+
}
56+
});
57+
},
58+
set: function set() {
59+
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
60+
61+
return dispatch({
62+
type: generateType(types.set, name),
63+
payload: {
64+
name: name,
65+
state: state
66+
}
67+
});
68+
}
69+
};
70+
},
71+
stateToProps: function stateToProps(state) {
72+
return {
73+
state: state.uiState
74+
};
75+
}
76+
};
77+
}

dist/reducer.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
7+
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; };
8+
9+
exports.default = reducer;
10+
11+
var _get = require('lodash/get');
12+
13+
var _get2 = _interopRequireDefault(_get);
14+
15+
var _immutabilityHelper = require('immutability-helper');
16+
17+
var _immutabilityHelper2 = _interopRequireDefault(_immutabilityHelper);
18+
19+
var _ = require('./');
20+
21+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22+
23+
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; }
24+
25+
// this reducer handles all state changes for the uiState slice
26+
function reducer() {
27+
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
28+
var action = arguments[1];
29+
30+
// add initial state
31+
if (action.type === (0, _.generateType)(_.types.add, (0, _get2.default)(action, 'payload.name'))) {
32+
return _extends({}, state, _defineProperty({}, action.payload.name, action.payload.state));
33+
}
34+
35+
// shallow merge for state updates
36+
if (action.type === (0, _.generateType)(_.types.set, (0, _get2.default)(action, 'payload.name'))) {
37+
return (0, _immutabilityHelper2.default)(state,
38+
// if store not created with combinedReducers, assume state is top-level
39+
state[action.payload.name] ? _defineProperty({}, action.payload.name, { $merge: action.payload.state }) : { $merge: action.payload.state });
40+
}
41+
42+
return state;
43+
}

0 commit comments

Comments
 (0)