Last active
May 31, 2018 10:34
-
-
Save javimosch/32a3ab9822875f327293bc194ca72f3f to your computer and use it in GitHub Desktop.
Minimal React Flux Library (React che)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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')); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I created a formal library from this prototype.
https://github.com/javimosch/react-che