├── .gitignore ├── Connector.js ├── GenerateReducer.js ├── README.md ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules -------------------------------------------------------------------------------- /Connector.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | 3 | export const FilterObjectKeys = (obj, predicate) => 4 | Object.keys(obj) 5 | .filter(key => predicate(key)) 6 | .reduce((res, key) => (res[key] = obj[key], res), {}) // eslint-disable-line 7 | 8 | export default function (Component, States = [], Actions = []) { 9 | const mapStateToProps = state => { 10 | const states = FilterObjectKeys(state, (key) => States.indexOf(key) > -1) || {} 11 | return states 12 | } 13 | const mapDispatchToProps = dispatch => { 14 | let actionMap = {} 15 | Actions.forEach(action => { 16 | actionMap[action.name] = (...args) => dispatch(action(...args)) 17 | }) 18 | return actionMap 19 | } 20 | 21 | const connected = connect( 22 | mapStateToProps, 23 | mapDispatchToProps 24 | )(Component) 25 | 26 | return connected 27 | } 28 | -------------------------------------------------------------------------------- /GenerateReducer.js: -------------------------------------------------------------------------------- 1 | const buildState = (state, action) => { 2 | let data = { ...action } 3 | delete data.type 4 | const reduction = data.reducer ? data.reducer(state, data) : data 5 | if (Array.isArray(reduction)) { 6 | return reduction 7 | } else { 8 | return { ...state, ...reduction } 9 | } 10 | } 11 | 12 | export default function (type, defaultState, hook) { 13 | const handle = (state = defaultState, action) => { 14 | if (action.type === type) { 15 | const newState = buildState(state, action) 16 | return hook ? hook(newState, action) : newState 17 | } 18 | 19 | return state 20 | } 21 | 22 | return handle 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Diluter 2 | An automatic Redux reducer, taking the pain out of Redux. 3 | ___ 4 | ## Install 5 | ``` 6 | npm i --save Diluter 7 | ``` 8 | 9 | ## Usage 10 | ### Create a Store 11 | ```js 12 | import Diluter from 'Diluter' 13 | 14 | const defaultState = { 15 | USER: { name: '', id: 0 }, 16 | FRIENDS: [] 17 | } 18 | 19 | const store = Diluter(defaultStore) 20 | ReactDOM.render( 21 | 22 | 23 | 24 | , document.getElementById('root')) 25 | ``` 26 | 27 | ### Connect a Component 28 | ```js 29 | import { Connector } from 'Diluter' 30 | 31 | class App extends Component { 32 | render() { 33 | return ( 34 |
35 | Hello {this.props.USER.name}! 36 | You have {this.props.FRIENDS.length} friends. 37 |
38 | ) 39 | } 40 | } 41 | 42 | // Connector takes 3 arguments: 43 | // 1. The React Component 44 | // 2. The keys of the store to pass as props 45 | // 3. Actions to be mapped with dispatch as props 46 | export default Connector(App, ['USER', 'FRIENDS'], []) 47 | ``` 48 | 49 | ### Dispatching an Action 50 | ```js 51 | const setUserName = name => dispatch => { 52 | dispatch({ 53 | type: 'USER', // The key to target in the store 54 | name // Automatically apply the name to the "USER" object 55 | }) 56 | } 57 | 58 | class SetName extends Component { 59 | render() { 60 | return ( 61 |
62 | Please choose a new name. 63 | this.props.setUserName(e.target.value)} 67 | /> 68 |
69 | ) 70 | } 71 | } 72 | 73 | // "setUserName" needs to recieve a dispatch function to 74 | // dispatch changes to the store. This is automatically 75 | // mapped to all functions passed in the 3rd argument, 76 | // after which they're passed to the component via props. 77 | export default Connector(SetName, ['USER'], [setUserName]) 78 | ``` 79 | 80 | ### Action with Custom Reducer 81 | ```js 82 | // In this case, friend is an object containing 83 | // arbitrary data about the user. Not 84 | const addFriend = friend => dispatch => { 85 | dispatch({ 86 | type: 'FRIENDS', 87 | reducer: (state) => { 88 | // This function will determine the new state 89 | // of the "FRIENDS" object in the store instead 90 | // of the usual automatic reducer built into Diluter. 91 | 92 | // Append the friend object to the store (FREINDS object) 93 | return [...state, friend] 94 | } 95 | }) 96 | } 97 | ``` 98 | 99 | ### Hook into the Reducer 100 | ```js 101 | // The hook allows you to modify reductions after the 102 | // normal reducer but before it gets applied to the store. 103 | const hook = (newState, action) => { 104 | // Set a value with the key "lastAction" containing 105 | // the action in the store before applying it. 106 | return { ...newState, lastAction: action } 107 | } 108 | const store = Diluter(defaultState, hook) 109 | ``` -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, combineReducers, compose } from 'redux' 2 | import thunk from 'redux-thunk' 3 | import GenerateReducer from './GenerateReducer' 4 | import Connector from './Connector' 5 | 6 | export default (defaultState, hook, customMiddlewares) => { 7 | const mappedReducers = {} 8 | Object.keys(defaultState).forEach(key => { 9 | mappedReducers[key] = GenerateReducer(key, defaultState[key], hook) 10 | }) 11 | 12 | let reducers = combineReducers(mappedReducers) 13 | let middlewares = [thunk, ...customMiddlewares] 14 | let store = createStore(reducers, compose( 15 | applyMiddleware(...middlewares), 16 | window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : a => a 17 | )) 18 | return store 19 | } 20 | 21 | export { Connector, GenerateReducer } 22 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diluter", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/runtime": { 8 | "version": "7.3.4", 9 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz", 10 | "integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==", 11 | "requires": { 12 | "regenerator-runtime": "^0.12.0" 13 | } 14 | }, 15 | "hoist-non-react-statics": { 16 | "version": "3.3.0", 17 | "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", 18 | "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", 19 | "requires": { 20 | "react-is": "^16.7.0" 21 | } 22 | }, 23 | "invariant": { 24 | "version": "2.2.4", 25 | "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", 26 | "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", 27 | "requires": { 28 | "loose-envify": "^1.0.0" 29 | } 30 | }, 31 | "js-tokens": { 32 | "version": "4.0.0", 33 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 34 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 35 | }, 36 | "loose-envify": { 37 | "version": "1.4.0", 38 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 39 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 40 | "requires": { 41 | "js-tokens": "^3.0.0 || ^4.0.0" 42 | } 43 | }, 44 | "object-assign": { 45 | "version": "4.1.1", 46 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 47 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 48 | }, 49 | "prop-types": { 50 | "version": "15.7.2", 51 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 52 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 53 | "requires": { 54 | "loose-envify": "^1.4.0", 55 | "object-assign": "^4.1.1", 56 | "react-is": "^16.8.1" 57 | } 58 | }, 59 | "react-is": { 60 | "version": "16.8.4", 61 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.4.tgz", 62 | "integrity": "sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==" 63 | }, 64 | "react-redux": { 65 | "version": "6.0.1", 66 | "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-6.0.1.tgz", 67 | "integrity": "sha512-T52I52Kxhbqy/6TEfBv85rQSDz6+Y28V/pf52vDWs1YRXG19mcFOGfHnY2HsNFHyhP+ST34Aih98fvt6tqwVcQ==", 68 | "requires": { 69 | "@babel/runtime": "^7.3.1", 70 | "hoist-non-react-statics": "^3.3.0", 71 | "invariant": "^2.2.4", 72 | "loose-envify": "^1.4.0", 73 | "prop-types": "^15.7.2", 74 | "react-is": "^16.8.2" 75 | } 76 | }, 77 | "redux": { 78 | "version": "4.0.1", 79 | "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz", 80 | "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==", 81 | "requires": { 82 | "loose-envify": "^1.4.0", 83 | "symbol-observable": "^1.2.0" 84 | } 85 | }, 86 | "redux-thunk": { 87 | "version": "2.3.0", 88 | "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", 89 | "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" 90 | }, 91 | "regenerator-runtime": { 92 | "version": "0.12.1", 93 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz", 94 | "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" 95 | }, 96 | "symbol-observable": { 97 | "version": "1.2.0", 98 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", 99 | "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diluter", 3 | "version": "1.0.2", 4 | "description": "An automatic Redux reducer, taking the pain out of Redux.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/mobooru/Diluter.git" 12 | }, 13 | "keywords": [ 14 | "Redux", 15 | "React", 16 | "Reducer" 17 | ], 18 | "author": "RekkyRek", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/mobooru/Diluter/issues" 22 | }, 23 | "homepage": "https://github.com/mobooru/Diluter#readme", 24 | "dependencies": { 25 | "react-redux": "^6.0.1", 26 | "redux": "^4.0.1", 27 | "redux-thunk": "^2.3.0" 28 | } 29 | } 30 | --------------------------------------------------------------------------------