Plugins allow you to add
statePlugins
selectors
- query the statereducers
- modify the stateactions
- fire and forget, that will eventually be handled by a reducer. You can rely on the result of async actions. But in general it's not recommendedwrapActions
- replace an action with a wrapped action (useful for hooking into existingactions
)
components
- React componentsfn
- commons functions
To add a plugin we include it in the configs...
SwaggerUI({
url: 'some url',
plugins: [ ... ]
})
Or if you're updating the core plugins.. you'll add it to the base preset: src/core/presets/base/index.js
Each Plugin is a function that returns an object. That object will get merged with the system
and later bound to the state.
Here is an example of each type
// A contrived, but quite full example....
export function SomePlugin(toolbox) {
const UPDATE_SOMETHING = "some_namespace_update_something" // strings just need to be uniuqe... see below
// Tools
const fromJS = toolbox.Im.fromJS // needed below
const createSelector = toolbox.createSelector // same, needed below
return {
statePlugins: {
someNamespace: {
actions: {
actionName: (args)=> ({type: UPDATE_SOMETHING, payload: args}), // Synchronous action must return an object for the reducer to handle
anotherAction: (a,b,c) => (system) => system.someNamespaceActions.actionName(a || b) // Asynchronous actions must return a function. The function gets the whole system, and can call other actions (based on state if needed)
},
wrapActions: {
anotherAction: (oriAction, system) => (...args) => {
oriAction(...args) // Usually we at least call the original action
system.someNamespace.actionName(...args) // why not call this?
console.log("args", args) // Log the args
// anotherAction in the someNamespace has now been replaced with the this function
}
},
reducers: {
[UPDATE_SOMETHING]: (state, action) => { // Take a state (which is immutable) and an action (see synchronous actions) and return a new state
return state.set("something", fromJS(action.payload)) // we're updating the Immutable state object... we need to convert vanilla objects into an immutable type (fromJS)
// See immutable about how to modify the state
// PS: you're only working with the state under the namespace, in this case "someNamespace". So you can do what you want, without worrying about /other/ namespaces
}
},
selectors: {
// creatSelector takes a list of fn's and passes all the results to the last fn.
// eg: createSelector(a => a, a => a+1, (a,a2) => a + a2)(1) // = 3
something: createSelector( // see [reselect#createSelector](https://github.com/reactjs/reselect#createselectorinputselectors--inputselectors-resultfunc)
getState => getState(), // This is a requirement... because we `bind` selectors, we don't want to bind to any particular state (which is an immutable value) so we bind to a function, which returns the current state
state => state.get("something") // return the whatever "something" points to
),
foo: getState => "bar" // In the end selectors are just functions that we pass getState to
}
}
... // you can include as many namespaces as you want. They just get merged into the 'system'
},
components: {
foo: ()=> <h1> Hello </h1> // just a map of names to react components, naturally you'd want to import a fuller react component
},
fn: {
addOne: (a) => a + 1 // just any extra functions you want to include
}
}
}
The plugin factory gets one argument, which I like to call
toolbox
. This argument is the entire plugin system (at the point the plugin factory is called). It also includes a reference to theImmutable
lib, so that plugin authors don't need to include it.
Each plugin you include will end up getting merged into the system
, which is just an object.
Then we bind the system
to our state. And flatten it, so that we don't need to reach into deep objects
ie: spec.actions becomes specActions, spec.selectors becomes specSelectors
You can reach this bound system by calling getSystem
on the store.
getSystem
is the heart of this whole project. Each container component will receive a spread of props from getSystem
here is an example....
class Bobby extends React.Component {
handleClick(e) {
this.props.someNamespaceActions.actionName() // fires an action... which the reducer will *eventually* see
}
render() {
let { someNamespaceSelectors, someNamespaceActions } = this.props // this.props has the whole state spread
let something = someNamespaceSelectors.something() // calls our selector, which returns some state (either an immutable object or value)
return (
<h1 onClick={this.handleClick.bind(this)}> Hello {something} </h1> // render the contents
)
}
}
TODO: a lot more elaboration `