1、Hooks深入
React Hooks 带来的好处不仅是 “更新粒度更细,代码更清晰,没有嵌套”,还有如下三个特性:
- 多个状态不会产生嵌套,写法还是平铺的(renderProps 可以通过 compose 解决,但使用略为繁琐,而且因为强制封装一个新对象而增加了实例的数量)。
- Hooks 可以引用其他 Hooks。
- 更容易将组件的 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 的一些思考 中有一句话叫做有状态的组件没有渲染,有渲染的组件没有状态。
useFriendStatusBoolean与useFriendStatusString是有状态的组件(使用useState),没有渲染(返回非 UI 的值),这样就可以作为 Custom Hooks 被任何 UI 组件调用。
FriendListItem 与 FriendListStatus 是有渲染的组件(返回了 JSX),没有状态(没有使用 useState),这就是一个纯函数 UI 组件,
2、Hooks额外带来的影响
Hooks 带来的约定
Hooks函数要求必须用"use"开头,因为这样可以方便EsLint去做检查,防止用状态condition判断包裹useHooks语句。React Hooks 并不是通过 Proxy 或者 getters 实现的,而是通过数组来实现的,每次useState都会改变下标,如果useState被包裹在condition中的话,那可能就会出现,每次执行的下标对不上,导致useState的更新数据出错。
这其实就是"约定大于配置",在java的springboot早就出现了这样的概念,但在React中还是第一次引入"约定优先"的理念。限制代码命名和书写顺序看似降低了自由度,增加了学习成本,但其实它带来了前所未有的便利,避免了因为书写或者个人的代码书写风格习惯出现的问题。
nextjs 和umi 都通过有 “约定路由” 的功能,都有大大降低了路由配置复杂度,这也是一种"约定大于配置"的理念。那么 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:
- useState
- useEffect
- useContext
- useReducer
- useCallback
- useMemo
- useRef
- useImpretiveHandle
- useLayoutEffect
- useDebugValue
React Redux从7.1开始支持hooks:
- useSelector
- useDispatch
- useStore
React Route 从5.1版本开始支持hooks:
- useHistory
- useLocation
- useParams
- useRouteMatch
当然,hooks远远不只有这些,但是自定义hooks都是基于这些基础的hooks之上来进行封装的。所以这些基础的、常用的hooks是必须要掌握的。
另外,推荐两个hooks库:
- ahooks 阿里官方出品的hooks库
- hooks-guide github上开源的一个自定义hooks的库,访问可能过慢
\