“你们好,我是谷阿莫,今天要将的是一个...”,哈哈哈,看到这个题我就想到这个开头。最近react 官方在 2018 ReactConf 大会上宣布 React v16.7.0-alpha(内测) 将引入 Hooks。因此咱们有必要了解 Hooks,以及由此引起的疑问。html
固然,学习的最好、最直接的方法就是看文档,因此我也很是建议你们去看文档学习,并且仍是官方的文档而不是中文版的文档。本文也是楼主在学习事后的一些总结与思考,楼主会把最近学习到的由浅入深,按部就班,尽量简洁的分享给你们,但愿对你们有帮助。不足之处可在评论区补充,本文讲从如下几个大方面来展开:react
react官方给出的动机是用来解决长时间使用和维护react过程当中遇到的一些难以免的问题。好比:api
在进一步了解以前,咱们须要先快速的了解一些基本的 Hooks 的用法。数组
Hooks让咱们的函数组件拥有了相似类组件的特性,好比local state、lifecycle,并且还解决了上面提到的一系列问题,它是如何解决这些问题的,下面会在一一指出。首先来快速的看看Hoos的使用,这里讲最主要的两个 Hooks :useState 和 useEffect。先看一个你可能看过不少遍的例子app
import { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <p> {count} </p> <button onClick={() => setCount(count + 1)}> Click me </button> ); }
useState 这个方法能够为咱们的函数组件带来 local state,它接收一个用于初始 state 的值,返回一对变量ide
const [count, setCount] = useState(0); // 等价于 var const = useState(0)[0]; // 该state var setConst = useState(0)[1]; // 修改该state的方法
useEffect 能够利用咱们组件中的 local state 进行一些带有反作用的操做函数
useEffect(() => { document.title = `You clicked ${count} times`; });
useEffect 中还能够经过传入第二个参数来决定是否执行里面的操做来避免一些没必要要的性能损失,只要第二个参数数组中的成员的值没有改变,就会跳过这次执行。若是传入一个空数组 [ ],那么该 effect 只会在组件 mount 和 unmount 时期执行。工具
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 若是count没有改变,就跳过这次执行
useEffect 中还能够经过让函数返回一个函数来进行一些清理操做(clean up),好比取消订阅等性能
useEffect(() => { api.subscribe(theId); return () => { api.unsubscribe(theId) //clean up } });
useEffect 何时执行? 它会在组件 mount 和 unmount 以及每次从新渲染的时候都会执行,也就是会在 componentDidMount、componentDidUpdate、componentWillUnmount 这三个时期执行。学习
清理函数(clean up)何时执行? 它会在前一次 effect执行后,下一次 effect 将要执行前,以及 Unmount 时期执行
咱们只能在 函数组件 中使用 Hooks,咱们也能够在一个组件中使用多组 Hooks。好比:
function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { API.subscribe(props.friend.id); return () => { API.unsubscribe(props.friend.id); }; }); return isOnline }
可是这里有一点须要咱们注意的就是 咱们只能在顶层代码(Top Level)中调用 Hooks,不能在循环或判断语句等里面调用,这样是为了让咱们的 Hooks 在每次渲染的时候都会按照 相同的顺序 调用,由于这里有一个跟关键的问题,那就是 useState 须要依赖参照第一次渲染的调用顺序来匹配对于的state,不然 useState 会没法正确返回它对于的state。
好了,知道了 Hooks 基本使用后,咱们就能够来了解 Hooks 是怎么解决 react 长期存在的问题的。
过去对于相似问题的解决方案主要有两个:
<DataProvider render={data => ( <h1>Hello {data.target}</h1> )}/>
function getComponent(WrappedComponent) { return class extends React.Component { constructor(props) { super(props); } componentDidMount() { // doSomething } componentWillUnmount() { // doSomething } render() { return <WrappedComponent {...this.props} />; } }; }
可是不管是哪种方法都会形成组件数量增多,组件树结构的修改,并且有可能出现组件嵌套地狱(wrapper hell)的状况。如今 React 经过 custom Hooks 来解决这个问题。
custom Hooks 并非一个api,而是一个规则。具体实现就是经过一个函数来封装跟状态有关的逻辑(stateful logic),将这些逻辑从组件中抽取出来。在这个函数中咱们可使用其余的 Hooks,也能够单独进行测试,甚至将它贡献给社区。
import { useState, useEffect } from 'react'; function useCount() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return count }
好比上面的一个例子,他就是一个 custom Hooks,提取了对 count 的操做。这里须要遵循一个约定,命名要用 use*
,这是为了方便咱们区分,利于咱们维护。能够看到他其实就是一个函数,咱们能够在现有的全部其余组件中引用它
function CountStatus() { const count = useCount(); return count; }
这里的核心概念就是将逻辑提取出来封装在 custom Hooks,而后能够在任何的其余组件中共享这部分逻辑,也能够贡献给社区。因此我也预测在不久的未来,会出现不少的充满想象力的各类用途的 custom Hooks 在社区中出现,极大的提升咱们的开发效率。
前面咱们也提到,咱们的组件可能会随着开发的进行变得愈来愈复杂,要处理愈来愈多的 local State,那么在组件的生命周期函数中就会充斥着各类互不相关的逻辑,这里须要引入官方的比较复杂的例子,先看基于之前类组件的状况:
class FriendStatusWithCounter extends React.Component { constructor(props) { super(props); this.state = { count: 0, isOnline: null }; this.handleStatusChange = this.handleStatusChange.bind(this); } componentDidMount() { document.title = `You clicked ${this.state.count} times`; ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); } handleStatusChange(status) { this.setState({ isOnline: status.isOnline }); } // ...
通过 Hook 改造后:
function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); function handleStatusChange(status) { setIsOnline(status.isOnline); } // ... }
状态和相关的处理逻辑能够按照功能进行划分,没必要散落在各个生命周期中,大大下降了开发和维护的难度。除了这几个hooks还有其余额外的hooks,在此继续了解 Hooks API Reference
hooks让咱们的函数组件的功能获得了扩充,拥有了和类组件类似的功能,甚至避免了类组件存在的各类问题,那么就会出现各类的疑问,好比
Our goal is for Hooks to cover all use cases for classes as soon as possible. There are no Hook equivalents to the uncommon getSnapshotBeforeUpdate and componentDidCatch lifecycles yet, but we plan to add them soon.It is a very early time for Hooks, so some integrations like DevTools support or Flow/TypeScript typings may not be ready yet. Some third-party libraries might also not be compatible with Hooks at the moment.
官方的目标是尽量快的让 Hooks 去覆盖全部的类组件案例,可是如今 Hooks 还处于一个很是早的阶段,各类调试工具、第三方库等都尚未作好对 Hooks 的支持,并且目前也没有能够取代类组件中 getSnapshotBeforeUpdate 和 componentDidCatch 生命作起的 Hooks,不过很快会加上他们。总的来时就是鼓励你们在之后使用 Hooks,对于已存在的类组件没必要大规模的去重写,Hooks及Hooks的生态会继续完善,请期待。
Often, render props and higher-order components render only a single child. We think Hooks are a simpler way to serve this use case. There is still a place for both patterns (for example, a virtual scroller component might have a renderItem prop, or a visual container component might have its own DOM structure). But in most cases, Hooks will be sufficient and can help reduce nesting in your tree.
大概意思就是,在大多数案例下,hooks 足够应付且更适合,因此优先考虑 hooks。
这是我看到 hooks 后比较关心的两个问题,若是你们想了解更多的问题的话能够到 Hooks FAQ 了解。若是有什么不足或要补充的地方,欢迎评论区提出。