「React」Hooks入门介绍篇(二)

285 阅读4分钟

1、Hooks深入

React Hooks 带来的好处不仅是 “更新粒度更细,代码更清晰,没有嵌套”,还有如下三个特性:

  1. 多个状态不会产生嵌套,写法还是平铺的(renderProps 可以通过 compose 解决,但使用略为繁琐,而且因为强制封装一个新对象而增加了实例的数量)。
  2. Hooks 可以引用其他 Hooks。
  3. 更容易将组件的 UI 与状态分离。

第一点很容易理解,因为,多个状态可使用多个useState就可以,也可以结合起来为一个state,具体看useState章节。

重点说明第二点和第三点。

Hooks可以引用其他Hooks,以及UI和状态逻辑组件分离。

首先,我们可以先自定义一个hooks,用来处理判断是否在线的逻辑

// 底层 Hooks, 返回布尔值:是否在线
function useFriendStatusBoolean(friendID) {
  const [isOnline, setIsOnline] = useState(null);
​
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
​
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });
​
  return isOnline;
}

然后,根据是否在线的hooks,我们可以另外再定义一个Hooks,根据是否在线,返回不同的字符串

// 上层 Hooks,根据在线状态返回字符串:Loading... or Online or Offline
function useFriendStatusString(props) {
  const isOnline = useFriendStatusBoolean(props.friend.id);
​
  if (isOnline === null) {
    return "Loading...";
  }
  return isOnline ? "Online" : "Offline";
}

之后,UI组件可以根据底层Hooks,也就是是否在线的状态,去渲染成不同的颜色

// 使用了底层 Hooks 的 UI
function FriendListItem(props) {
  const isOnline = useFriendStatusBoolean(props.friend.id);
​
  return (
    <li style={{ color: isOnline ? "green" : "black" }}>{props.friend.name}</li>
  );
}

最后,UI组件还可以根据上层Hooks返回不同的字符串,去渲染不同的字符串

// 使用了上层 Hooks 的 UI
function FriendListStatus(props) {
  const statu = useFriendStatusString(props.friend.id);
​
  return <li>{statu}</li>;
}

对 React Hooks 的一些思考 中有一句话叫做有状态的组件没有渲染,有渲染的组件没有状态。

  • useFriendStatusBooleanuseFriendStatusString 是有状态的组件(使用 useState),没有渲染(返回非 UI 的值),这样就可以作为 Custom Hooks 被任何 UI 组件调用。

FriendListItemFriendListStatus 是有渲染的组件(返回了 JSX),没有状态(没有使用 useState),这就是一个纯函数 UI 组件,

2、Hooks额外带来的影响

Hooks 带来的约定

Hooks函数要求必须用"use"开头,因为这样可以方便EsLint去做检查,防止用状态condition判断包裹useHooks语句。React Hooks 并不是通过 Proxy 或者 getters 实现的,而是通过数组来实现的,每次useState都会改变下标,如果useState被包裹在condition中的话,那可能就会出现,每次执行的下标对不上,导致useState的更新数据出错。

这其实就是"约定大于配置",在java的springboot早就出现了这样的概念,但在React中还是第一次引入"约定优先"的理念。限制代码命名和书写顺序看似降低了自由度,增加了学习成本,但其实它带来了前所未有的便利,避免了因为书写或者个人的代码书写风格习惯出现的问题。

nextjsumi 都通过有 “约定路由” 的功能,都有大大降低了路由配置复杂度,这也是一种"约定大于配置"的理念。那么 React Hooks 就像代码级别的约定,大大降低了代码复杂度。

状态与 UI 的界限会越来越清晰

React官方很早就提出了容器组件和UI组件的概念,但是在工程化实践中并没有多少人会遵守这个概念。很大一部分原因是因为class组件的逻辑过于分散,不适合做逻辑的复用。

Hooks与生俱来的特性就比较适合做逻辑复用,只要一个hooks函数不产生UI,那么他就永远可以被其他hooks封装。虽然有副作用,但是都会被包含在useEffect中。

Hooks需要集中在React函数活其他自定义hooks函数的的顶部书写,也很容易去养成逻辑组件和UI组件抽离的书写习惯。可以更好的践行"有状态的组件没有渲染,有渲染的组件没有状态"这个理念。

3、使用hooks需要遵循两条规则。

1. **只在最顶层用hooks,不要在循环,条件或嵌套函数中调用 Hook。**
   - 确保hooks在每一次的渲染都是找相同的顺序被调用,这可以让React在多次调用中保持状态的正确
2. **只在React函数中调用hooks**
     - 不在普通的js函数中调用hooks
     - 可以在React函数组件中调用hooks
     - 也可以在定义hooks中调用其他hooks

react官方从16.8开始了支持hooks,并且提供了10个基础hooks:

三大基础hooks:

  1. useState
  2. useEffect
  3. useContext
  4. useReducer
  5. useCallback
  6. useMemo
  7. useRef
  8. useImpretiveHandle
  9. useLayoutEffect
  10. useDebugValue

React Redux从7.1开始支持hooks:

  1. useSelector
  2. useDispatch
  3. useStore

React Route 从5.1版本开始支持hooks:

  1. useHistory
  2. useLocation
  3. useParams
  4. useRouteMatch

当然,hooks远远不只有这些,但是自定义hooks都是基于这些基础的hooks之上来进行封装的。所以这些基础的、常用的hooks是必须要掌握的。

另外,推荐两个hooks库:

  • ahooks 阿里官方出品的hooks库
  • hooks-guide github上开源的一个自定义hooks的库,访问可能过慢

\