首发于useReact
Hook

Hook

更好的阅读体验可以到 yuque

这个不是一篇 React Hook 入门的文章,而是介绍它是在什么场景上使用。如果还不知道 React Hook 是什么,可以到 React Hook 官方文档 上看。

前言


看了很多组件库,大家都是在自定义组件,是偏视图层的。直到遇见了 downshift,通过 Render Props 的方式来写组件,可以做到不关心 View 层,只关心逻辑处理,这样子复用性和灵活性太高了。Ant Design 的 Select 组件和 Fusion 的 Select 组件都是可以基于上面来实现的。

所以我一直想做的是 Headless Component,是一种没有视图层的组件,它是定义着一种交互行为或者说是逻辑处理。真正的组件应该是“交互行为” + “视图”,视图太容易改变了,每一个公司或者每一个部门都要搞一个 Design,但是其实很多交互方式不变,只是 Props 定义和视图变了。

等到 React 推出来 Hook 之后,真的很喜欢 ,让 Function Component 有了状态机了。重点是每一个状态机都是抽离复用的,你完全可以定义一个属于你自己的状态机,那是不是可以利用 hook 去定义一些交互行为或者通用逻辑处理呢?在之前先让我们了解下 Hook 的一些特性。

概述


在我之前的一些回答上有讲到:

通过 Hooks API,我觉得 React Team 很好得把 UI 编程抽象分类成 State,Context,Effect,Function了。然后每一个 Hook 都是可插拔的,又将会是一个繁荣的hook社区

Hook + Function Component 其实就是 UI 编程,然后再加上 Context 作为通知机制,写起代码不能再爽。Hook 的一些特性如下:

  • Persistent:不断执行函数,可以保持对应的数据;
  • Rerender:可以触发组件的更新;
  • Composability:可以在 Hook 嵌套使用其他 Hook;
  • Headless:跟 UI 隔离,关心状态和副作用管理;


我们可以利用 Hook 特性再结合我们的场景做一些事情。

场景


想想我们的现实生活,“开关”真的无处不在,比如水龙头 、电灯 等,他们只是展示不一样,但是其实他们的逻辑处理其实是一样,都是两个状态在来回切换。还有红绿灯 ,是三个状态在无限闭环切换。

回到我们的 Web ,其实前端组件也存在“界面展示不一样,状态管理逻辑其实是一样”的情况出现。


useToggle

只有两个状态的无限切换
  1. 勾选:用在 Checkbox 组件再合适不过了;
  2. 树展开折叠:树展开和折叠两个状态切换;
  3. Switch:antdfusion 都有 Switch 组件;

useStep

任意状态有特定间隔互斥地切换
  • Step:Step 组件,点击下一步到其他步骤,也可以切换到之前完成的步骤
  • 轮播:很明显它也是一步或者跳步地切换,跟 Step 组件很类似。只是展示不一样,但是交互其实是一致的。下面是 weibo.com 首页的“轮播”,下面的小黑点▪️是可以点击切换到其他新闻的。
  • Counter:计数器也是一步一步增加或者减少,间隔为 1 而已
  • 分页器:上一页,下一页是一页一页跳转,但是它也可以跳转到特定页


  • Tab:切换到 tab2 或者 tab3 是按 Tab 为单位跨 Tab 或者一个个 Tab 切换
  • 放大&缩小:对于图片放大 ,也是按照一定的间隔来切换的


上面例子可以看到我们其实在 Web 开发存在很多界面展示不一样,但是背后逻辑是一致的情况出现。还有很多类似的情况可以让我们去挖掘的。

useAsync

同步方式写异步

在 Web 编程上还有一个重要的概念是异步,离不开通过异步获取数据来展示内容。异步编程从“callback”==> “Promise” ==> “generator”==> “async & await”,其实就是越来越对开发者友好,用“同步方式写异步”。

React 提供的 useEffect 可以让我们在 React 编程里面实现“同步方式写异步”。

之前

class SerivceComponent extends React.Compnent {
  state = { data: {} }
  componentDidMount() {
    getData().then((res) => {
      this.setState({ data: res.data })
    })
  }
  render() {
    return JSON.stringify(data)
  }
}

之后

const SerivceComponent = () => {
  const { data = {} } = useService(getData);
  return JSON.stringify(data);
}


Hook 可以让一个组件变成一个无限状态机,不断执行这个组件,再加上缓存状态和副作用独立管理。

上面讲的是一些场景用独立 Hook 实现之后带来的效应,但是在 React 社区里还存在一个很重要的话题就是“状态管理”,如何结合 Hook 来做到状态管理呢?

状态管理

⚠️ 注意:在我的定义下“状态管理”跟“组件通信”是同一个概念。

原因

首先是为什么需要状态管理呢?我们来分析下前端角色定位,前端本质是一个展示与交互共存的领域。React setState 和 JSX 机制让我们可以通过数据 State 来控制展示内容,而交互其实就是基于事件的联动,通过触发事件从而来控制其他组件界面和状态更新。通过 State 和 setState 两个概念就可以解决前端面临的很多问题,而 State 和 setState 在 React 上有两种存在方式,一种是直接在组件内部管理的,一种是通过 Hook 的方式抽离。


那现在 React 的现状是什么呢?又存在什么问题呢?

现状

  • React 本来就是一个很强大的状态机;
  • 它存在的问题是 State 和 逻辑 的分享;

React 状态管理单个组件内部很轻松,State 和 setState 就可以搞定。但是如果是 State 升级到跨组件的场景就有点棘手,不管是父子通信,兄弟通信还是无相关的组件之间通信。

下面我们再对“状态管理”的问题进行分类。

分类


  • 内部:直接用 useState 或者 this.setState 解决;
  • 父子:包含跨层级;
  • 兄弟:有血缘关系;
  • 不相干:毫无有血缘关系,但一定会有一个同一个 Root;


状态管理一定要具备的两点能力:

  • 组件里面获取其他组件的数据;
  • 组件里面可以触发其他组件数据修改并且 Rerender;


也就是如果你只想在你这个组件用到这个状态的话,那你内置 State 和 setState 到组件内部就行。但是如果要让其他组件用到该组件的 State 和 setState 能力的话,那就要把该组件的 State 和 setState 能力共享出来。

那怎么共享出来呢?共享的前提是要能独立 State 和 setState,很明显 Hook 具备这样子的能力。现在问题就是组件怎么去获取 Hook 了。React 提供两个方案,一个是 Props,另外一个是 Context。React 组件就是一棵树,最后都会有一个统一的 Root 节点,我们可以在最顶层用同一个的节点来中介,帮我们通信。为了解决 Props 地狱,我们通过 Context 的方式来共享状态。也就是我们可以用到 Context + Hook 来实现状态管理。

受到 constate 和 unstated 的启发,我造了一个轮子为 Conuse。

主要功能是方便多个 Hook 聚合一起使用,因为 Hook 抽离得越细,React 更新组件会更准确,React 只会更新用到这个 Hook 的组件。

参考

编辑于 2019-10-09 23:02