First of all I want to tell you that this librarie will fix your below concerns about your project:
your confusing to find action string and constants in your reducers and your action creators.
your worries about your application scaffold, all of us always think that our application doesn't have rubost scaffold.
this is redux-peach
do exactly.
redux-peach is a wrapper. It helps you to understand states and manage states with redux and also helps you to use powerfull libraries like redux-action and redux-thunk in your projects easier, with immutated states.
I think react-boilerplate is highly scalable and it predict all project situations and solve problems. In this boilerplate you see that every part of project will be load separately and will load with lazy loading. I mean when you need Component Box that time this will load, not earlier.
In this boilerplate Huge part of project or screens or part of screens or etc is in Containers. Containers Contain Components, routes, actions, reducers, selectors and other things that container need. So now we will make a reducer and actions in one component and it works fine.
many times we need to define one action in Component A and handle defined action in B,C,D Components.
It's good solution that handle this action in reducer of other components but we know that use redux without redux-actions
is messy and we have first problem that I mentioned above.
If we use redux-actions
we can't handle one action with two or more function and we have to make separate actions and this will make application scaffold weak.
redux-peach
have both of this we can handle actions with two or more functions to run, with same action name and clean scaffold in addition you can use redux middlewares every where you want and it's very simple.
This is multi purpose library and not written to use for specific frameworks like reactjs, vue, angular or etc. This can globaly use for javascript projects.
This contain middlewares and enhancers for your store, if you want to add
some action logger or redux dev tools or etc, you should add as argument to store.configure
method.`
We have State type, this will strongly handle states and make states immutable.Ease of use immutable states.
rootState will added when you want to configure your store, simply you will write javascript object but it will change to State instance.
Return function that accepts some arguments like rootState, middlewares and enhancers and return redux store object
This class where you should make and instance for your application and then configure it with your rootState, middlewares and enhancers.
You should create action for your application an then dispatch, ofcurse you need to add listener for your action, every thing about actions is in Action class.
We have two type of actions and some sub actions:
-
Regular actions
Regular actions contains two sub actions, [actionName_SUCCEED, actionName_FAILED]
-
Async actions
Async actions contains four sub actions, [actionName_STARTED, actionName_Succeed, actionName_Ended, actionName_Failed]
According to your action type (async flag) some sub actions will dispatch that you can handle by some function like this: sub action[actionName_SUCCEED] -> dispatch -> will handle with setOnSucceedListener function
sub action[actionName_FAILED] -> dispatch -> will handle with setOnFailedListener function
Some libraries has their own actions, you can handle them by create new action with same name and catch them with .setOnHappenedListener(x => x)
method.
$ npm install --save redux-peach
# redux is a peer-dependency of redux-peach so let's install it.
$ npm install --save redux
Import redux-peach
in Your project if you are using react js you can use it
with react-redux
package and use its provider
component.
const {Store, State, Action} = require('redux-peach')
const store = new Store()
You can pass redux store to Store constructor or let redux-peach to make new redux store.
note: If you want to add your own redux store Please pay attention that redux-peach needs redux-thunk middleware, so you should add it to your store middlewares.
Now you should configure your store like this:
// set middlewares and enhancers here
store.configure({
rootState: { users: { list: ['Jim', 'Jack', 'Paul'] }, Counter: 0 },
middlewares: [],
enhancerts: [],
})
Create Async action with name INCREMENT:
First Appearance of action means to define an action with some functionality, don't worry actions are mutable and throughout application you can change their behavior.
new Action()
.setName('INCREMENT')
.hookToStore(store)
.setScope('Math.Numbers') // o.O Scope? What is it? I will explain it!
.setInitialState({ Counter: 0 })
.setAsyncFlag(true)
.setOnDispatchListener(
value =>
new Promise(resolve => setTimeout(() => resolve(value * value), 1000)),
)
.make()
// You can add payloadCreator and metaCreator as described in redux-actions library.
})
After 1000 millisecond will return square of value that you will dispatch.
Always you should call make
, hookToStore
to make and hook current action
to store.
It's important to call make
method after all operations of action.
And It's also better to call hookToStore
after setName
because in
hookToStore
if action defined before, this will return that and if you call
it as last method and action name has been defined before, all of your
configurations will discard.
Now See INCREMENT action in other parts of project:
Second or more appearance of action means you want to change action behavior, maybe you want to add some listener or make action async or etc...
Action('INCREMENT')
// you can call Action like function and pass actionName as first argument and
// store as second argument. Or you can use `new` keyword like first expression
.hookToStore(store)
.setOnSucceedListener((action, state) => ({
Counter: state.Counter + action.payload,
}))
.make()
})
Look at this action, what is it mean? yes mutate INCREMENT action but it's so weird!?
It seams we have to setOnSucceedListener and it means that we over write it, but NO! if we set listeners for times, redux-peach will run all of them in FIFO queue.
Action('INCREMENT', store)
.setOnSucceedListener((action, state) => ({
Counter: state.Counter + action.payload + action.payload,
}))
.make()
// Action find return function like preparation for dispatch as first
// argument and action object as second.
const [increment, action] = Action.find('INCREMENT', store)
// Set default status based on what you defined in action definition.
State.set(action.getInitialState(), store)
store.dispatch(increment(4)) //dispatch INCREMENT Action with.increment by 4
Create DECREMENT
action that will dispatch immediately after created.
Action()
.setName('DECREMENT')
.hookToStore(store)
.setScope('Math.Numbers')
.setOnDispatchListener(value => value * 2)
.setSelfDispatchFlag(true)
.setOnDispatchArgs([66])
.setOnSucceedListener((action, state) => ({
Counter: state.Counter - action.payload,
}))
.make()
If you need to set state every where you want, you can but be careful that this feature create to set default state in lazy loading for components.
State.set({ test: 'redux-peach' }, store)
// OR
store.setState({ test: 'redux-peach' })
If we want to look on states we see this:
setTimeout(() => console.log(store.state), 200)
//After 200miliseconds:
{ users: { list: [ 'Jim', 'Jack', 'Paul' ] },
Math: { Numbers: { Counter: -132 } },
test: 'redux-peach' }
setTimeout(() => console.log(store.state), 1001)
//After 801miliseconds:
{ users: { list: [ 'Jim', 'Jack', 'Paul' ] },
Math: { Numbers: { Counter: -84 } },
test: 'redux-peach' }
If you think deeper about actions and more deeper about store states you can see that we can make horrible side effects, how?
If you do not define scope for your action, on your succeedListener you can change states for example about User information while your action is about Math and countering, but you do this unwelcome mess.
While you are using scope concept your state object in Listeners functions will point to that specific scope for better development experience.
So setScope
help you that your action work around this scope in store state.
and
prevent making unwanted side effects.
As I mentioned above, if you have store with very special configurations like
persistence or some development tools like redux-devtools or etc, you can make
your store then import to redux-peach
.Just pass your store to constructor.
You should just do these two steps:
- while store creating pass
rootReducer
as reducer parameter tocreateStore
.
import {rootReducer} from 'redux-peach'
const store = createStore(rootReducer({}), ...)
- add redux-thunk middleware to your store while store creation.
And then run this:
const store = new Store(reduxStore)
To use this library with react you can create store object and pass through your components with Context or create global object or every ways you will prefer.
Create Store.js file in your project root:
import { Store, State, Action } from 'redux-peach'
//Create your own reduxStore...
export const store = new Store(reduxStore)
export const setState = newState => State.set(newState, store)
export const findAction = actionName => Action.find(actionName, store)
export const newAction = actionName => Action(actionName, store)
Now import every where you want in your react project
You can create context in react and provide your store in root of your project and consume it every where.Or until react 16.x you can use [legacy context](https://reactjs .org/docs/legacy-context.html) see sample code below.
If you are using react-redux store
object will provide with provider
component.Just consume it
class A extends Component {
static contextTypes = {
store: PropTypes.object,
}
constructor(props, context) {
super(props, context)
this.store = context.store
}
}
State
types provide shallow equals in work with redux-react
package. and
it does not cause a side effects and performance issue.
Run tests with npm test
.
Test framework is jest.
50 Tests passed.
Please feel free to send me feed backs, I needs them. and so I will be so happy if you contribute in project.
MIT