{"id":23035807,"url":"https://github.com/blackchef/rce","last_synced_at":"2025-08-14T17:31:36.387Z","repository":{"id":13827735,"uuid":"75061829","full_name":"blackChef/rce","owner":"blackChef","description":"rce stands for react, data cursor, elm, is a lightweight react architecture.","archived":false,"fork":false,"pushed_at":"2022-12-04T18:00:18.000Z","size":2021,"stargazers_count":14,"open_issues_count":10,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-29T00:09:28.386Z","etag":null,"topics":["architecture","data-cursor","react"],"latest_commit_sha":null,"homepage":"https://blackchef.github.io/rce/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/blackChef.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-11-29T08:52:17.000Z","updated_at":"2020-12-19T05:26:59.000Z","dependencies_parsed_at":"2023-01-11T19:45:14.145Z","dependency_job_id":null,"html_url":"https://github.com/blackChef/rce","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blackChef%2Frce","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blackChef%2Frce/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blackChef%2Frce/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blackChef%2Frce/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blackChef","download_url":"https://codeload.github.com/blackChef/rce/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229847826,"owners_count":18133644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["architecture","data-cursor","react"],"created_at":"2024-12-15T16:47:05.467Z","updated_at":"2024-12-15T16:47:06.110Z","avatar_url":"https://github.com/blackChef.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 问题\n- 我们喜欢使用组件，因为组件将功能封装在内部，我们不需要了解细节，引入它就可以使用。\n- 我们喜欢开发组件，因为组件化能让我们一次专注于解决一个问题。\n- 我们喜欢 mvc 架构，因为我们总是会遇到让不同地方渲染相同的状态的场景。\n\n但一个拥有本地状态，自己能控制状态更新的组件是没法和其他组件共享状态的。\n\n让一个组件能把功能封装，做到即插即用的同时，又能被外界控制，能和其他组件共享状态，就是 rce 解决的问题。\n\n\n# rce\nrce 代表 react, cursor, elm。是一个轻量级的 react 架构。它有以下几个特点:\n\n- 没有复杂的概念，没有复杂的代码。仅有两个 api，设计思路与 react 一致。会 react 就能快速上手 rce。\n- 利用数据指针，让你能把组件的 state 保存 app 的最上一层，但又能让将管理 state 的方法写在组件内部。\n- 每个组件都按下面的模式划分。 遵循这个受 elm 启发的模式，你就能轻易写出可高度复用的组件。\n  - init: 定义组件的默认状态。\n  - view: 渲染 model。发布 action。\n  - update: 响应 view 传来的 action，修改 model。\n\n\n[查看这个教程](https://github.com/blackChef/rce/blob/chinese-doc/tutorial/01.md)。了解 rce 的设计理念和实现方式。\n\n\n# 示例\n- 示例：https://blackchef.github.io/rce/\n- 示例用到的代码: https://github.com/blackChef/rce/tree/master/demo/components\n\n\n# 安装\n```\nnpm install rce-pattern --save\n```\n```\nyarn add rce-pattern\n```\n\n# 参考\n\n### model, init, view, update\n- model: Cortex data。组件的 state 以 cortex data 保存和传递。\n- init: Function。返回组件的默认 model 的值。\n- view: React Component。\n- update：Function。`update({ type, payload, model, dispatch, getLastetModel })`\n  - type: String。action 的类型。\n  - payload: Any。dispatch 发来的信息。\n  - model: Cortex Cursor。update 执行时，组件内的 model。\n  - getLastetModel: Function。获取组件最新的 model。在异步处理的 callback 内应该用它来获取最新的 model。\n  - dispatch: Function。可以在 update 内 dispatch 其他 action。\n\n### createComponent\n`createComponent({ name, view, update })` 一个 HOC，将 view 和 update 串联起来。\n\n- name: String, 非必须。组件的 displayName。有利于调试。\n- view: React Component，必须。\n- update: Function，非必须。\n\n\n传入 `createComponent` 的组件收到 `model`, `dispatch`, `dispatcher`,  三个额外的 props。\n\n- model：Cortex data。\n- dispatch: Function。`dispatch(type, payload)`。发布一个 action。\n  - type: String，必须。\n  - payload：Any，非必须。\n- dispatcher: Function。`dispatcher(type, arg)`。dispatcher 返回一个执行 dispatch 的函数。\n  - arg 为 undefine 时，返回 `event =\u003e dispatch(type, event)`。\n  - arg 为 Function 时，返回 `event =\u003e dispatch(type, arg(event))`。\n  - arg 为 其它时，返回 `() =\u003e dispatch(type, arg)`。\n\n### createModelHolder\n`createModelHolder(view, arg)`\n- arg 为函数时，用那个函数返回的值作为 view 的初始 model。\n- arg 为其他时，用 arg 作为 view 的初始 model。\n\n\n# 快速开始\n\n### CortexJs\nrce 采用 [cortexjs](https://github.com/mquan/cortex) 实现的数据指针。\n\n\u003e Cortex is an immutable data store for managing deeply nested structure with React\n\n考虑 `model = { a: { foo: 5 }, b: 5 }` 这样一个数据。 在将它创建成 cortex 数据之后：\n- 要读取 `model.a.foo` 的值，我们这么做：`fooValue = model.a.foo.val()`。\n- 要修改 `model.a.foo` 的值。我们这么做：`model.a.foo.set(10)`。\n\n修改 cortex 数据的操作是异步的。当 cortex 数据更新时，rce 会自动渲染你的 view。就跟 react state 的工作方式一样。\n\n### A Counter\n我们来编写一个 Counter 组件。在线例子：https://blackchef.github.io/rce/#/counter\n\n```\nimport React from 'react';\nimport createComponent from 'rce-pattern/createComponent';\n\n// name 用作 component 的 displayName， 有利于调试。\nlet name = 'counter';\n\n// 一个函数，返回这个组件的初始状态。\nlet init = function() {\n  return 0; // count\n};\n\n// 组件在其内部调用 dispatch 来发布 action。\n// update 函数接收到 action，再根据不同的 action 以不同的方式更新组件的 model。\nlet update = function({ type, model }) {\n  // update 只是一个函数。在 type 很多时，可以用各种各样的技巧来解决 too many ifs 的问题。\n  if (type === 'increment') {\n    // 这里用 set 和 val 两个函数来修改、读取 model 的值。\n    model.set( model.val() + 1 );\n  } else {\n    model.set( model.val() - 1 );\n  }\n};\n\n// view 是一个 react 组件。被 createComponent wrap 之后，它收到 model, dispatch, dispatcher 三个属性。\n// model：Cortex Cursor。组件的 model，理解为组件的 state。\n// dispatch(type, payload): dispatch 触发 action。\n// dispatcher(type, arg): dispatcher 返回一个执行 dispatch 的函数。 有助于编写 function 形式的 react 组件\nlet view = function ({ model, dispatch, dispatcher }) {\n  return (\n    \u003cdiv\u003e\n      \u003cbutton type=\"button\" onClick={dispatcher('increment')} \u003e+\u003c/button\u003e\n      \u003cspan\u003e{model.val()}\u003c/span\u003e\n      \u003cbutton type=\"button\" onClick={dispatcher('decrement')}\u003e-\u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n\n// createComponent 将 view，update 二者串联起来。\nview = createComponent({ name, update, view });\nexport { init, view };\n```\n\n### Three Counters\n现在我们利用之前的 Counter 组件，编写一个包含三个 Counter，其中一个独立，另外两个共享状态的新组件： ThreeCounters。\n在线例子：https://blackchef.github.io/rce/#/threeCounters\n\n```\nimport React from 'react';\nimport createComponent from 'rce-pattern/createComponent';\nimport { view as Counter, init as counterInit } from './counter';\n\nlet name = 'threeCounters';\n\nlet init = function() {\n  return {\n    // 一个父组件可以用子组件的 init 函数来生成子组件需要的 model。\n    // 这时候它不需要管那个子组件需要的 model 是怎样的数据结构，有怎样的值。\n    countA: counterInit(),\n\n    // 父组件也能用其他值为子组件设定默认值。\n    countBC: 1,\n  };\n};\n\n// 因为 update 存在于组件自身内部，一个父组件可以简简单单的引入一个组件，那个组件本身就能工作。\n// 又因为父组件可以接触到子组件的 model，父组件也可以在父一级对子组件进行控制。\nlet update = function({ model }) {\n  // 只有一种 type 的 action。这时候就没必要对 type 进行判断。\n  model.set( init() );\n};\n\n// 将 model 的子 model 传给不同的子组件。共享同一子 model 的组件会有相同的渲染。\nlet view = function ({ model, dispatch, dispatcher }) {\n  return (\n    \u003cdiv\u003e\n      \u003csection className=\"section\"\u003e\n        \u003ch4\u003ecounterA\u003c/h4\u003e\n        \u003cCounter model={model.countA} /\u003e\n      \u003c/section\u003e\n\n      \u003csection className=\"section\"\u003e\n        \u003ch3\u003ecounterB and counterC share same model\u003c/h3\u003e\n\n        \u003csection\u003e\n          \u003ch4\u003ecounterB\u003c/h4\u003e\n          \u003cCounter model={model.countBC} /\u003e\n        \u003c/section\u003e\n\n        \u003csection\u003e\n          \u003ch4\u003ecounterC\u003c/h4\u003e\n          \u003cCounter model={model.countBC} /\u003e\n        \u003c/section\u003e\n      \u003c/section\u003e\n\n      \u003csection className=\"section\"\u003e\n        \u003cbutton\n          type=\"button\"\n          onClick={dispatcher('reset')}\n        \u003e\n          reset all\n        \u003c/button\u003e\n      \u003c/section\u003e\n    \u003c/div\u003e\n  );\n};\n\nview = createComponent({ name, update, view });\nexport { init, view };\n```\n\n### Model Holder\n\n在 app 的最上层使用 createModelHolder，将整个 app 内的 model 都保存在这里。\n```\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport createModelHolder from 'rce-pattern/createModelHolder';\nimport { view as ThreeCounters, init as threeCountersInit } from './threeCounters';\n\n// createModelHolder(view, init): 把 model 保存在 state 里。\nlet App = createModelHolder(ThreeCounters, threeCountersInit);\n\nReactDOM.render(\n  \u003cApp/\u003e\n  document.querySelector('.appContainer')\n);\n\n```\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackchef%2Frce","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblackchef%2Frce","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackchef%2Frce/lists"}