Skip to content

Instantly share code, notes, and snippets.

@javimosch
Last active May 31, 2018 10:34
Show Gist options
  • Save javimosch/32a3ab9822875f327293bc194ca72f3f to your computer and use it in GitHub Desktop.
Save javimosch/32a3ab9822875f327293bc194ca72f3f to your computer and use it in GitHub Desktop.
Minimal React Flux Library (React che)
/*
HOW IT WORKS?
- Library object is che
- Add actions using che.defineActions(["JUMP","CLICK","SUBSCRIBE","ETC"])
- Add stores using che.defineStore ... (see example below)
- Bind stores using che.store.bind
FEATURES
- Automatic unbind uppon component unmount
- Async actions work out of the box
RESTRICTIONS
- Unique store names
- Unique action names
*/
import React from 'react';
import { render } from 'react-dom';
/*SUBSCRIBE ACTIONS (CONFIGURATION PHASE)*/
let COMPONENTS_STORE_LISTENERS = {};
let STORE_ACTION_LISTENERS = {};
let STORES = {};
let ACTIONS = {};
let che = {
STORES:STORES,
stores:{}//.e.g. stores.backend.getState()
};
che.start = ()=>{
che.store = cheStore();
che.action = cheAction();
}
che.defineActions = function(newActions){
for (var x in newActions){
if (typeof ACTIONS[newActions[x]] !== 'undefined'){
throw Error('che: duplicated action ' + newActions[x]);
}
ACTIONS[newActions[x]] = newActions[x];
}
}
/*CALL ACTIONS (FROM COMPONENTS)*/
function cheAction(){
var availableActions = {};
function createActionHandler(name){
availableActions[name] = async () => {
let availableListeners = STORE_ACTION_LISTENERS[name];
if (availableListeners) {
for (var x in availableListeners) {
availableListeners[x]();
}
}
}
}
for(var x in ACTIONS){
createActionHandler(ACTIONS[x]);
}
return availableActions;
}
/*SUBSCRIBE STORES (CONFIGURATION PHASE)*/
che.defineStore = function (name,state,handler) {
if (typeof STORES[name]!=='undefined'){
throw Error('che: '+name+' store is duplicated')
}
STORES[name] = name;
che.stores[name]={
getState:()=>state
};
//function storeListenerHandler()
var scope = {
on:(()=>{
var actions = {}
//Acciones disponibles .e.g. SALUDAR
for(var x in ACTIONS){
//.e.g. action.on.SALUDAR(state=>{})
actions[ACTIONS[x]]=((actionName)=>{
return (listener)=>{
STORE_ACTION_LISTENERS[actionName] = STORE_ACTION_LISTENERS[actionName] ||[];
let storeListener=async()=>{
await listener.apply({},[state]);
let availableComponentStoreListeners = COMPONENTS_STORE_LISTENERS[name];
if(availableComponentStoreListeners){
for(var i in availableComponentStoreListeners){
availableComponentStoreListeners[i](state);
}
}
}
STORE_ACTION_LISTENERS[actionName].push(storeListener)
}})(ACTIONS[x])
}
return actions;
})()
};
handler(scope);
}
/*BIND / UNBIND STORES (FROM COMPONENTS)*/
function cheStore(){
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
function updateState(cmp,name,state){
cmp.setState({
[name.toLowerCase()]: state
});
}
return {
bind:(cmp,stores)=>{
let storeName;
let unbindListeners = [];
for(var x in stores){
storeName = stores[x];
if(typeof STORES[storeName]=='undefined'){
throw Error('che: unknown store '+storeName)
}
COMPONENTS_STORE_LISTENERS[storeName] = COMPONENTS_STORE_LISTENERS[storeName] ||[];
COMPONENTS_STORE_LISTENERS[storeName].push((state)=>{
updateState(cmp, storeName,state);
});
unbindListeners.push(((length) => {
//Original length after push
return () => COMPONENTS_STORE_LISTENERS[storeName].splice(length - 1, 1);
})(COMPONENTS_STORE_LISTENERS[storeName].length));
let initialState = che.stores[storeName].getState();
updateState(cmp, storeName, initialState);
}
var componentWillUnmountOriginal = cmp.componentWillUnmount;
cmp.componentWillUnmount = function(){
for (var x in unbindListeners) unbindListeners[x]();//remove listeners.
componentWillUnmountOriginal && componentWillUnmountOriginal();
}
}
}
}
//------------------
//LETS ADD AN ACTION
che.defineActions(["TOGGLE_BLOCK"])
//LETS ADD AN STORE
che.defineStore('Toggle', {
block:true
},(action) => {
//LETS BIND THE ACTION
action.on.TOGGLE_BLOCK((state)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
state.block = !state.block;
resolve(state);
},1500);
});
})
})
che.start();
//---------------------
class Block extends React.Component{
name='Block'
render(){
return (<div>
<h4>Block</h4>
</div>)
}
}
class App extends React.Component {
name='App'
componentWillMount(){
//LETS BIND THE STORE (UNBIND AUTO)
che.store.bind(this, [che.STORES.Toggle])
}
render(){
let state = this.state, props = this.props
let block = state.toggle.block? <Block/> : "";
return (<div>
<br />
State:
<br />
{JSON.stringify(state)}
<br />
Props:
<br />
{JSON.stringify(props)}
<br />
<br />
<button onClick={che.action.TOGGLE_BLOCK}>Saludar</button>
<br />
<br />
{block}
</div>
)
}
}
render(<App foo="1" />, document.getElementById('root'));
@javimosch
Copy link
Author

I created a formal library from this prototype.

https://github.com/javimosch/react-che

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment