基于redux、react函数式组件,简单、健壮、强代码组织的框架
基于redux、react函数式组件,简单、健壮、强代码组织的框架
deef从“响应一个个用户事件”出发,进而抽象成状态数据及数据流转,最后表现成UI渲染,达成交互
状态决定展现,交互就是改状态:model state => UI render => callback handler => model reducer
$ cd examples/xxx
$ npm install
$ npm start
参见 examples文件夹
import deef from 'deef';
// 1. Create app
const app = deef();
// 2. Register models
const model = {};
app.model(model);
// 3. Connect components with state
const App = app.connect(getUIState, callbacks)(UI);
// 4. Start app, onRendered在App渲染后触发,可以理解为入口callback
app.start('#root', App, onRendered);
app.connect在业务代码里非常常用,推荐将const app = deef()等逻辑放到单独一个模块,并配置webpack alias以方便引用,可参考examples
const model = {
// namespace是区别不同model的唯一标识
namespace: 'count',
// 定义这个model的状态数据
state: {
num: 0
},
// 定义改状态的接口
reducers: {
add(state, action) {
// reducers里的function是纯函数,输入当前state和action,输出nextState
// action是callback handler里dispatch的,action要求{type: 'modelNamespace/reducerName', payload: xxx}的格式
// 比如这里action是{type: 'count/add', payload: 1}
return {
...state,
num: state.num + action.payload
};
}
}
};
redux的约定:在任何地方都不可以通过引用直接改当前状态,如state.num=1是不对的
const UI = ({num, tab, ...callbacks}) => {
// 第一个参数是外部传入的props,包括展现依赖的state和响应交互的callbacks,要求通过es6解构的方式直观取出依赖的状态,把callbacks放到后面,如果callbacks少的话,就直接摆出来,如果大于两个的话,要求使用"...callbacks"的方式,将callbacks整合,然后再解构这个callbacks。
const {onAdd, onSwitchTab} = callbacks;
// 拿到一个UI组件,看前两行就直观知道该UI依赖的所有的state和callbacks。
return (<div>
<h1>{num}</h1>
<button onClick={onAdd}></button>
<div>);
};
UI是纯函数组件(stateless functional component),不可以直接处理交互,要通过callbacks暴露出去
const getUIState = (store, ownProps) => {
// store是所有model的状态汇聚成的store,是plain object,第一层是model的namespace
// ownProps是引用组件时显式传入的props,如<UI stateX="test" />中的stateX
// 如果UI依赖的stateX需要根据model里定义的stateY、stateZ等计算所得,且该计算逻辑较为复杂,或者这个计算逻辑是可复用的,需将该逻辑放到Component/selector.js
return {
// 要求return一个plain object,会注入到UI的props
num: store.count.num,
// 可以整合配置里的一些常量进去
...config.VALIDATION_RULES
}
};
const callbacks = {
// 这些callbacks会注入到UI 命名必须是onXXX的形式
onAdd({dispatch, getState}, event) {
// 固定传入第一个参数{dispatch, getState}
// dispatch方法传入action{type: 'model/reducer', payload: {}}来调用model的reducer改变model中的状态
// getState().modelNamespace即可拿到某一model的状态
// event是UI组件调用时传入的第一个参数,有更多的参数,依次往后排
dispatch({type: 'count/add', payload: event.target.value});
},
// 可以把一些公共的callbacks抽出来,放到Component/handler.js里,方便多处复用
...commonCallbacks
};
require('app').connect(getUIState, callbacks)(UI)