16 |
17 | const withName = withState([], {name: 'Dan'})
18 |
19 | const AWithName = withName(A)
20 | const BWithName = withName(B)
21 |
22 | let tree = renderer.create().toJSON()
23 | expect(tree).toMatchSnapshot()
24 |
25 | tree = renderer.create().toJSON()
26 | expect(tree).toMatchSnapshot()
27 | })
28 |
29 | test('withState should accept a pre-middleware function', () => {
30 | const A = ({ name }) =>
Hello, {name}!
31 | let done = false
32 | const middleware = (state, action) => { done = true }
33 |
34 | const Wrapped = withState([], {}, middleware)(A)
35 | const component = renderer.create()
36 | let tree = component.toJSON()
37 |
38 | dispatcher.dispatch({type: 'FAKE_ACTION', content: {}})
39 |
40 | expect(tree).toMatchSnapshot()
41 | expect(done).toBeTruthy()
42 | })
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## T-Redux
2 |
3 | This is a mini library that implements the Redux pattern.
4 | Useful if you don't need all the stuff that the redux framework gives you.
5 |
6 | [](https://circleci.com/gh/emadb/t-redux/tree/master)
7 |
8 | ### How to use it
9 | 1) Install the library
10 | ```
11 | npm install t-redux
12 | ```
13 |
14 | 2) Import in your files
15 | ```
16 | import {withState, dispatcher, buildReducer} from 't-redux'
17 | ```
18 |
19 | 3) What you get is
20 | - A High Order Component to wrap your React components
21 | - An action dispatcher
22 | - An utility function to build the reducers
23 |
24 | ### One file example
25 |
26 | ```javascript
27 | // import the needed modules
28 | import {withState, dispatcher, buildReducer} from 't-redux'
29 |
30 | // this is a PORC (Plain Old React Component)
31 | class MyCounter extends React.Component {
32 | constructor() {
33 | super()
34 | this.plusOne = this.plusOne.bind(this)
35 | }
36 | plusOne() {
37 | // Dispacth the action (the content is optional)
38 | dispatcher.dispatch({type: 'PLUS_ONE', content: this.props.counter})
39 | }
40 | render() {
41 | return (
42 |
43 |
Click count: {this.props.counter}
44 |
45 |
)
46 | }
47 | }
48 | // Build the reducers as a map ACTION:(state, action) => state
49 | const reducers = buildReducer({
50 | 'PLUS_ONE': (state, action) => ({counter: state.counter + 1})
51 | })
52 |
53 | // Define the initial state
54 | const INITIAL_STATE = { counter: 0 }
55 |
56 | const middlewareFn = (state, action) => {
57 | // inspect the state or the action. Useful for logging and debugging or to store the events.
58 | }
59 |
60 | // export the wrapped component passing the reducers and the initial state
61 | export default withState([reducers], INITIAL_STATE, middlewareFn)(MyCounter)
62 | ```
63 |
64 | ### API
65 |
66 | #### `buildReducer(map)`
67 | Build the reducers map. The map is composed by an action type and a reducer function. The reducer function is f: (state, action) -> state
68 | The state is changed applying the action type reducer.
69 |
70 | #### `dispatcher`
71 | Is an object with three methods:
72 | ##### `register(fn)`
73 | Register the function fn in the list of subscribed functions. Returns the id of the registered entry.
74 | ##### `unregister(id)`
75 | Remove the function from the list of subscribers.
76 | ##### `dispatch(action)`
77 | Call every subscribed functions passing the action
78 |
79 | #### `withState(reducers, initialState, middleware)`
80 | Connect the magic. Returns a function that it can be used to connect a component with the reducers and the initial state. After the initial state it accept a function that will be called before applying the action.
81 |
82 |
83 | ### Why
84 | This package was born while I was learning react and redux. I love the principles behind Redux but I don't like boilerplate and overcomplicated code so I decided to try to write a simple implementation of the redux pattern.
85 | Feel free to use it or to continue to use the [Real One](https://github.com/reactjs/redux).
86 |
87 | [Michele Bertoli](https://github.com/MicheleBertoli) gave a [terrific presentation](https://speakerdeck.com/michelebertoli/setstate-ftw) on how to manage state in react applications. `t-redux` is one of the available options.
88 |
--------------------------------------------------------------------------------
/dist/withState.js:
--------------------------------------------------------------------------------
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 _dispatcher = require('./dispatcher');
16 |
17 | var _dispatcher2 = _interopRequireDefault(_dispatcher);
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
22 |
23 | 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; }
24 |
25 | 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; }
26 |
27 | function combineReducers(reducers, state, action) {
28 | var newState = reducers.reduce(function (acc, r) {
29 | var projection = r.project(acc);
30 | return Object.assign({}, acc, r(projection, action));
31 | }, state);
32 | return newState;
33 | }
34 |
35 | function withState() {
36 | var reducers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
37 | var initialState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
38 | var middleware = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {};
39 |
40 | return function (WrappedComponent) {
41 | return function (_React$Component) {
42 | _inherits(WithState, _React$Component);
43 |
44 | function WithState(props) {
45 | _classCallCheck(this, WithState);
46 |
47 | var _this = _possibleConstructorReturn(this, (WithState.__proto__ || Object.getPrototypeOf(WithState)).call(this, props));
48 |
49 | _this.state = { innerState: initialState };
50 | return _this;
51 | }
52 |
53 | _createClass(WithState, [{
54 | key: 'componentWillMount',
55 | value: function componentWillMount() {
56 | var _this2 = this;
57 |
58 | this.regId = _dispatcher2.default.register(function (action) {
59 | middleware(_this2.state.innerState, action);
60 | var nextState = combineReducers(reducers, _this2.state.innerState, action);
61 | _this2.setState({ innerState: nextState });
62 | });
63 | }
64 | }, {
65 | key: 'componentWillUnmount',
66 | value: function componentWillUnmount() {
67 | _dispatcher2.default.unregister(this.regId);
68 | }
69 | }, {
70 | key: 'render',
71 | value: function render() {
72 | return _react2.default.createElement(WrappedComponent, _extends({}, this.state.innerState, this.props));
73 | }
74 | }]);
75 |
76 | return WithState;
77 | }(_react2.default.Component);
78 | };
79 | }
80 |
81 | exports.default = withState;
--------------------------------------------------------------------------------