c++中两个key确定一个value_鲁迅:世上本只需要一个Modal组件

背景

本文旨在分享,React hook 在中大型中台项目中的实践,适合熟悉 React hook 用法的同学,希望能对你有帮助。

用到的库

1. unstated-next

200 bytes to never think about React state management libraries ever again.

永远不必再考虑 React 状态管理了,仅仅 200 字节的状态管理解决方案。

卡颂注:用React的同学可以去了解下unstatedunstated-next。看完这2个源码不到40行的状态管理库,你会感叹:React简单状态的管理本该这样。

unstated-next 主要是利用 React.createContext 状态共享,将需要注入 Provider 的状态以及状态更新操作抽象到 hook 中,提供给 Function Component 用的一个状态管理库。

ts源码只有 40 行。

源码分析

export function createContainer(useHook) {

  let Context = React.createContext<Value | typeof EMPTY>(EMPTY);

  function Provider(props) {

    let value = useHook(props.initialState);

    // 将 hook 返回值 暴露给 Provider 的 value

    return <Context.Provider value={value}>{props.children}</Context.Provider>;

  }

  function useContainer() {

    // 用 useContext 获取 Context 上传递的 value

    let value = React.useContext(Context);

    if (value === EMPTY) {

      throw new Error("Component must be wrapped with ");

    }

    return value;

  }

  return { Provider, useContainer };

}

2. use-immer

A hook to use immer as a React hook to manipulate state.

一个用于将 immer 作为 React hook 来操纵状态的 hook。

use-immer 可以将 state 数据 immutable,更新深层嵌套数据更为方便,且有函数编程的感觉。

const [value, setValue] = useImmer({

  a: { b: { c: { d: 12 } }, b2: { c: 34 } },

});

// 某些场景下改变嵌套层级很深的对象,或者对象数组中的某个值

setValue((draft) => {

  draft.a.b.c.d = 19;

  // draft[0].value = 23;

});

// 数组某个值的变化

setValue((draft) => {

  draft[2].name= 19;

});

3. sunflower-antd

一些流程组件的自定义 hook,例如useModal, useModalForm 等,提升效率明显。

项目实践

1. 烦不胜烦的 modal

在中台项目中,对一些列表的资源信息CRUD 弹窗是必不可少的,所以页面中table的管理必不可少,且很繁琐,容易混乱。起初我是这样

<ConfigModal ... />

<EditModal ... />

<RenameModal ... />

很麻烦,且重复搬砖的代码很多。最终我们用了 context 和 useHook 全局挂载激活的方式将 modal 和每个列表页解耦。下面我们逐步分析如何优雅的写modal。

Modal 的一次生命周期基本包括:

ff631272a95c772b7655404d32616adb.png

特点:

  • modal 的打开和关闭由用户操作决定。

  • 需要记录每次选中的数据,传给要操作的 modal。

  • 点击提交成功后都需要关闭 modal 和页面触发刷新的操作。

每次只有一个激活的modal和选中的数据一一对应,两者都是用操作的一瞬间确定的,且每次只有一个 modal 处于激活状态,所以用户的各种操作只是不断更新modal和data而已。

所以假如全局有一个的专门记录 modal 的地方,这样我们只需将用户要激活的modal不断替换,然后在全局的某处挂载当前激活的modal。

在使用modal的页面中,我们只需不断去更新全局记录值,当modal关闭时只需全局记录值置为空即可。这样在当前的页面中不需要再将烦人的众多modal一次次的引入,也不需要维护一系列的visible。

问题来了,那如全局记录值modal呢?聪明的你可能已经想到了context,没错就是它:

全局 Modal Context 记录的当前激活的 modal

const ModalContainer = createContainer(() => {

  const [modal, setModal] = useState<ReactNode>(null);

  return [modal, setModal] as const;

});

指定全局挂载modal的节点

const [modal] = ModalContainer.useContainer();

<div id="active-modal">{modal}div>;

激活当前要操作的 modal,我们自定义了useAction, 它的作用就是返回一匿名函数,他有两个参数key、 data,Key 和 modal 一一对应,data表示当前操作行的数据。

最后将data传入通过key确定的modal中,塞进全局的modalContext中。

// fn 是一个根据key返回对应 modal 的函数。

export function useAction(fn) {

  const [, setModal] = ModalContainer.useContainer();

  const fnRef = useRef(fn);

  // ...

  // key 标识对应的 modal, data为当前操作数据

  return (key, data) => {

    // 根据key确定返回正在操作的modal。

    const Result = fnRef.current(key, data);

    // 将 modal set进 context 里,就会激活modal显示。

    setModal(<Result data={data} />);

  };

}

接下来只需要,将useAction暴露给用户执行,通过传入的key和modal对应关系确定即将操作的modal,所以需要一一列举他们的对应关系。

我们自定义了 useActionCallback, 它接收列举所有 modal 的回调函数 fn,fn 根据传入的参数确定具体的 modal。

export function useActionCallback() {

  // 返回上面匿名函数 (key, data) => setModal();

  return useAction((key, data) => {

    switch (key) {

      case Operations.Create:

        return CreateModal;

      // ...

      default:

        return null;

  })

}

最终,在页面激活modal只需要如下调用即可:

const onAction = useActionCallback();

<Button type="primary" onClick={() => onAction(Operations.Edit, data)}>

  编辑

Button>;

至此,context 和 hook 让页面和 modal 解耦,它们的联系只有 data, 而 data 又作为参数随时可以传入。这优雅的写法,是不是让你耳目一新,心动了。

最后再看下modal的内幕,modal 的 visible 参数默认是true,当setModal后挂载,它才会被弹出显示。

当 modal 关闭时,需要将全局挂载的 modal 置空,所以把全局ModalContainer记录的modal置空即可。

// useActionModal 自定义hook 主要获取 modalProps

// modal关闭事件中置空

afterClose() {

  // ...

  setModal(null);

},

// ...

总结

react hook 和 context 结合会发生一些不可思议的事情。

  • context 的发明就是为了父子孙...组件间共享数据、全局记录数据。

  • Provide 负责传递共享的数据,useContext 负责消费数据,这里的消费包括使用、更新和删除等操作。

  • context 和 react hook 可以让页面和一些重复的操作做一些解耦合操作。


f3e4d3852c1fe5c2056a4fc9aa0838fc.pngf3e4d3852c1fe5c2056a4fc9aa0838fc.pngf3e4d3852c1fe5c2056a4fc9aa0838fc.png

关注我,送你一本源码学习手册

加入全网最大React源码学习社群

bfd53d896b2ff211eef3537120a9d631.png

70663f731cb36f9621ac406e33c9c28e.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值