Respo Hooks 写法的原由和示例

近期对 Respo 的状态管理方案进行了一次更新,
具体的代码能够看 https://github.com/Respo/resp...git

原由

Respo 的 States 方案为了方便热替换时保存状态, 作了一些限制,github

  • 组件挂载的时候以及渲染过程中不能 dispatch 事件,
  • states 以树形的方式存储, 须要手动调用 cursor-> 在组件之间传递分支,
  • 修改状态使用的路径, 也就是 cursor, 跟组件对应, 方便简写.

React Hooks 出来的时候, 我固然意识到了 Respo 功能的不足,
以前比较久了, 我为 Respo 加上了基础的 Effects, 能够加一些 DOM 操做,
React Hooks 能够把部分的组件状态抽到插件里去, Respo 不行.
Respo 的 cursor 是跟组件绑定的, 插件写法没法传递 cursor.app

另外一方面, 我在 Phlox 项目当中因为须要, 设计了个简单的 states tree 方案,
这个方案里 cursor 是须要用户手工传递的, 比较啰嗦, 可是勉强够用.
后来我仔细想一想, 这个方案对于 Respo 来讲, 也是够用的.
虽然写起来会啰嗦, 可是用户能够手动传递 cursor, 也就意味着能够拆成插件复用.函数

代码示例

文档上写得也不详细, 这边稍微再描述一下.
首先 Respo 的状态树, 大体上是这样一个结构,
其中 :data 专门用于存储节点的数据, :cursor 做为保留字段,
这就是一个树形节奏, 跟组件直接对应, 可是大体跟状态分支对应上:ui

{
  :cursor []
  :files {
    :ns {
      :add {
        :data {:show? false, :failure nil}
        :modal {
          :data {:text nil, :failure nil}
        }
      }
      "app.rude" {
        ":rmapp.rude" {
          :data {:show? false}
        }
      }
    }
    "app.rude" {
      :add {
        :data {:show? false, :failure nil}
        :modal {
          :data {:text nil, :failure nil}
        }
      }
    }
  }
}

须要更新状态时, 须要一个 cursor, 能够是 [:file :ns "app.rude" ":rmapp.rude"],
后面还有个 :data, 有了 cursor, 就能定位到数据作更新了.
后面大体想象, 组件用的就是一个分支的数据, 而 cursor 对应分支的路径.
具体在代码当中比较啰嗦, 不过比较容易能够维护二者的对应关系.
最终获得相似组件局部状态的一个效果.spa

为了方便书写, 我增长了一个 >> 函数, 把 states 和 cursor 一块儿传递.
通过杂七杂八的抽象之后, 最终获得这样效果的代码:
https://github.com/Respo/aler...
这中间是省略了好多的过程, 也不打算很详细描述了, 具体要看文档(还没补好).插件

相应地, 更新状态的部分我加了 update-states 函数, 做为一个简写.
状态更新做为 dispatch action 的一种特殊状况, 跟 dispatch 一块儿被处理.
若是没看以前, 我明确一下, Respo 当中 states 是跟 store 一块儿存储在全局的.
能分支读取, 能维护 cursor 作更新, store 当中能响应, 整个流程串起来了.设计

延伸的影响

增长这块功能主要的目标, 跟 React Hooks 相似, 为了逻辑的复用,
Respo 的组件跟 React 相似, 不容许从外部操做状态,
这就意味着我封装出来的 Modal 组件显得比较奇怪了, 或者说死板,
要么我外边维护一个 visible 状态, 传进去, 而且加上 on-change 作切换,
要么我把触发打开关闭的部分也放进组件里边, 这样使用起来就有点僵化了.code

而 Hooks 形态的写法, 开始容许状态被抽取到一个独立的函数当中,
好比我调整过的 prompt 用法, 就能够抽出的一个插件当中,
https://github.com/Respo/aler...事件

(defn use-prompt [states options]
  (let [cursor (:cursor states), state (or (:data states) {:show? false, :failure nil})]
    {:ui (comp-prompt-modal
          (>> states :modal)
          options
          (:show? state)
          (fn [text d!]
            (if (some? @*next-prompt-task) (@*next-prompt-task text))
            (reset! *next-prompt-task nil)
            (d! cursor (assoc state :show? false)))
          (fn [d!] (d! cursor (assoc state :show? false)) (reset! *next-prompt-task nil))),
     :show (fn [d! next-task]
       (reset! *next-prompt-task next-task)
       (d! cursor (assoc state :show? true)))}))

插件暴露 uishow 方法两部分, ui 用于渲染, show 方法用户更新状态.
这样, 以往代码当中至关多的弹层的逻辑就能够抽出作复用了.

后续代码会继续更新. 目前也认识到 Respo Hooks 相比 React Hooks 比较局限比较多,特别是 Effects 那块, React 作得比较强大了, Respo 这方面功能很弱.但愿目前来讲这个功可以用, 这样我能对 Calcit Editor 遗留的代码作一些整理.

相关文章
相关标签/搜索