- Notifications
You must be signed in to change notification settings - Fork14
matthewmueller/socrates
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Small (8kb), batteries-includedredux store to reduce boilerplate and promote good habits.
varStore=require('socrates')// create a storevarstore=Store(reducer)// subscribe to updatesstore.subscribe(listener)// dispatch an actionstore('change:user.name',{name:'an'})// supports implicit set for the 90% usecasestore('set:user.age',27)
npm install socrates
Redux pushed us forward in 2 key ways:
I. Promoting a single state architecture
II. Using an action dispatcher to update that state
The state management in Redux is verbose, but fantastic. Socrates aims to supplement Redux's state management to reduce keystokes and transparently combine a few confusing concepts together. Namely,combineReducer,FSA,redux-actions, andupdeep.
Socrates is only used to update state. Action dispatching is actually a much bigger part of application architecture than just state. Unfortunately, if you're new to Redux or just reading tutorials, you'll assume that actions are only used to update state.
You should be dispatching actions forall side effects. To make HTTP requests, setup Websockets, and set cookies. While you can do this in Redux's middleware, it's flow is mind-bending because Redux's middleware is synchronous, so you need to internally re-dispatch to achieve asynchronous behavior.
I have a version of middleware inspired by Koa's middleware done on the server that I'll be releasing soon to help you out with this.
Leave the the asynchrony to the action log (redux middleware). State changes that are rejected will throw errors.
Additionally, Socrates enforces that the returned result is aFlux Standard Action, so our actions all have the same format.
This greatly slight constraint goes a long ways towards better interoperability.
Wherever you can access state in Socrates it is frozen, or in other words, read-only. This eliminates any possibility of modified references causing unexpected changes to our state.
By freezing state only in development, it steers our code towards the immutable direction without handicapping performance in production.
In normal redux, reducers replace state. In socrates, they update state.
So instead of replacing your state like this:
functionreducer(state,action){returnObject.assign({},state,action.payload)}
You can simply return a diff:
functionreducer(state,action){return{phone:8675309}}
And Socrates will efficiently update the state using code inspired byupdeep. To remove a field, you can passnull
as the value.
Socrates includes an opinionated way to setup reducers, called areducer tree.
Reducer trees make it very easy to zero in on the reducers you want to operate on the dispatched action. For those of you familiar with redux and it's ecosystem, it's basically as ifcombineReducers andhandleActions had a child, where the functions are the actions and the objects are the state's shape.
Here's an example:
varstore=Socrates({// run with type: "boot"boot(state,action){returnaction},user:{// run with type: "update user"update(state,action){returnaction},settings:{// run with type: "change user.settings"change(state,action){// state & action only contain the relevate data// you only need to return an action, because socrates// updates, doesn't replace.// 3. state = { theme: "red" }// 4. action = { theme: "blue" }returnaction}}}})// 1. boot up socrates with our initial state// 2. change the user settingsstore({type:'boot',payload:{user:{name:'matt',age:26,settings:{theme:'red'}}}},{type:'change:user:settings',payload:{theme:'blue'}}})
If you don't like this approach, you can always just pass your custom reducer function into Socrates.
Create a store instance with an optional middleware array and a reducer.If the reducer is an object, it will create a reducer tree.
Dispatches an action. Dispatching can take on many forms:
// simple object dispatchsocrates({type:'name:change',payload:{name:'an'}})// using an event emitter stylesocrates('change:name',{name:'an'})// dispatch multiple object in seriessocrates({type:'change:name',payload:{name:'an'}},{type:'change:age',payload:{age:26}})// dispatch multiple object in parallelsocrates([{type:'change:name',payload:{name:'an'}},{type:'change:age',payload:{age:26}}])// using a function to dispatch an actionsocrates(function(state){return{type:'change:age',payload:{age:26}}})
Getting our state. This will be frozen in development
Subscribe to changes in our store
socrates.subscribe(function(new_state){// ... do something with the new state})
npm installmake test
MIT
About
Small (8kb), batteries-included redux store to reduce boilerplate and promote good habits.