rce stands for react, data cursor, elm, is a lightweight react architecture.
但一个拥有本地状态,自己能控制状态更新的组件是没法和其他组件共享状态的。
让一个组件能把功能封装,做到即插即用的同时,又能被外界控制,能和其他组件共享状态,就是 rce 解决的问题。
rce 代表 react, cursor, elm。是一个轻量级的 react 架构。它有以下几个特点:
查看这个教程。了解 rce 的设计理念和实现方式。
npm install rce-pattern --save
yarn add rce-pattern
update({ type, payload, model, dispatch, getLastetModel })
createComponent({ name, view, update })
一个 HOC,将 view 和 update 串联起来。
传入 createComponent
的组件收到 model
, dispatch
, dispatcher
, 三个额外的 props。
dispatch(type, payload)
。发布一个 action。dispatcher(type, arg)
。dispatcher 返回一个执行 dispatch 的函数。event => dispatch(type, event)
。event => dispatch(type, arg(event))
。() => dispatch(type, arg)
。createModelHolder(view, arg)
rce 采用 cortexjs 实现的数据指针。
Cortex is an immutable data store for managing deeply nested structure with React
考虑 model = { a: { foo: 5 }, b: 5 }
这样一个数据。 在将它创建成 cortex 数据之后:
model.a.foo
的值,我们这么做:fooValue = model.a.foo.val()
。model.a.foo
的值。我们这么做:model.a.foo.set(10)
。修改 cortex 数据的操作是异步的。当 cortex 数据更新时,rce 会自动渲染你的 view。就跟 react state 的工作方式一样。
我们来编写一个 Counter 组件。在线例子:https://blackchef.github.io/rce/#/counter
import React from 'react';
import createComponent from 'rce-pattern/createComponent';
// name 用作 component 的 displayName, 有利于调试。
let name = 'counter';
// 一个函数,返回这个组件的初始状态。
let init = function() {
return 0; // count
};
// 组件在其内部调用 dispatch 来发布 action。
// update 函数接收到 action,再根据不同的 action 以不同的方式更新组件的 model。
let update = function({ type, model }) {
// update 只是一个函数。在 type 很多时,可以用各种各样的技巧来解决 too many ifs 的问题。
if (type === 'increment') {
// 这里用 set 和 val 两个函数来修改、读取 model 的值。
model.set( model.val() + 1 );
} else {
model.set( model.val() - 1 );
}
};
// view 是一个 react 组件。被 createComponent wrap 之后,它收到 model, dispatch, dispatcher 三个属性。
// model:Cortex Cursor。组件的 model,理解为组件的 state。
// dispatch(type, payload): dispatch 触发 action。
// dispatcher(type, arg): dispatcher 返回一个执行 dispatch 的函数。 有助于编写 function 形式的 react 组件
let view = function ({ model, dispatch, dispatcher }) {
return (
<div>
<button type="button" onClick={dispatcher('increment')} >+</button>
<span>{model.val()}</span>
<button type="button" onClick={dispatcher('decrement')}>-</button>
</div>
);
};
// createComponent 将 view,update 二者串联起来。
view = createComponent({ name, update, view });
export { init, view };
现在我们利用之前的 Counter 组件,编写一个包含三个 Counter,其中一个独立,另外两个共享状态的新组件: ThreeCounters。
在线例子:https://blackchef.github.io/rce/#/threeCounters
import React from 'react';
import createComponent from 'rce-pattern/createComponent';
import { view as Counter, init as counterInit } from './counter';
let name = 'threeCounters';
let init = function() {
return {
// 一个父组件可以用子组件的 init 函数来生成子组件需要的 model。
// 这时候它不需要管那个子组件需要的 model 是怎样的数据结构,有怎样的值。
countA: counterInit(),
// 父组件也能用其他值为子组件设定默认值。
countBC: 1,
};
};
// 因为 update 存在于组件自身内部,一个父组件可以简简单单的引入一个组件,那个组件本身就能工作。
// 又因为父组件可以接触到子组件的 model,父组件也可以在父一级对子组件进行控制。
let update = function({ model }) {
// 只有一种 type 的 action。这时候就没必要对 type 进行判断。
model.set( init() );
};
// 将 model 的子 model 传给不同的子组件。共享同一子 model 的组件会有相同的渲染。
let view = function ({ model, dispatch, dispatcher }) {
return (
<div>
<section className="section">
<h4>counterA</h4>
<Counter model={model.countA} ></Counter>
</section>
<section className="section">
<h3>counterB and counterC share same model</h3>
<section>
<h4>counterB</h4>
<Counter model={model.countBC} ></Counter>
</section>
<section>
<h4>counterC</h4>
<Counter model={model.countBC} ></Counter>
</section>
</section>
<section className="section">
<button
type="button"
onClick={dispatcher('reset')}
>
reset all
</button>
</section>
</div>
);
};
view = createComponent({ name, update, view });
export { init, view };
在 app 的最上层使用 createModelHolder,将整个 app 内的 model 都保存在这里。
import React from 'react';
import ReactDOM from 'react-dom';
import createModelHolder from 'rce-pattern/createModelHolder';
import { view as ThreeCounters, init as threeCountersInit } from './threeCounters';
// createModelHolder(view, init): 把 model 保存在 state 里。
let App = createModelHolder(ThreeCounters, threeCountersInit);
ReactDOM.render(
<App></App>
document.querySelector('.appContainer')
);