React Hook和一些踩坑
React Hook是React 16.8的新增特性,它可以让我们在不编写class组件的情况下使用state以及生命周期函数等其他React特性。
State Hook
声明State
函数组件中,由于没有this
,所以不能使用this.state
来分配或读取State,我们要在组件中调用useState
Hook:
1 | // 声明一个count变量以及一个更新count的函数 |
调用useState
时,定义了一个State变量,这个变量叫做count
,以及一个更新count
的函数setCount
。useState
函数的唯一参数是初始State。
读取State
在函数组件中,我们直接用变量名就可以读取State:
1 | <p>You clicked {count} times</p> |
更新State
更新State需要用useState
返回的函数:
1 | <button onClick={() => setCount((count) => count + 1)}> |
Effect Hook
Effect Hook用于在函数组件中执行副作用操作:
1 | // 类似于componentDidMount和componentDidUpdate和componentWillUnmount三个函数的组合 |
无需清除的Effect
有时候我们想在React更新DOM之后运行一些额外的代码,这些都是无需清除的操作:
1 | // 每次DOM更新的时候更新标题 |
通过useEffect
这个Hook,我们告诉React组件需要在渲染后执行某些操作。React会保存传递的函数,称之为Effect,并在DOM更新后调用它。在默认情况下,Effect在第一次渲染后都会执行,不用再去考虑挂载还是更新,React保证了每次运行Effect的同时,DOM都已更新完成。
需要清除的Effect
如果Effect返回一个函数,React将会在卸载组件时调用它,(类似于componentWillUnmount
):
1 | // 组件卸载时会打印unmount |
Effect的清除阶段在每次重新渲染时都会执行,而不是只在卸载组件时执行一次。
跳过Effect进行性能优化
如果每次渲染后都执行清理或者执行Effect可能会导致性能问题,因此我们可以跳过Effect:如果某些特定的值在两次渲染之间没有发生变化,我们可以跳过对Effect的调用,只要传递数组做为useEffect
的第二个参数:
1 | // 只有count变化了才会执行 |
如果想要执行一个只运行一次的Effect,只需要将一个空数组做为第二个参数即可:
1 | // 只会运行一次 |
Hook规则
只在最顶层使用Hook
不要在循环、条件或嵌套函数中使用Hook,确保在React函数的最顶层和return
之前调用他们。
只在React函数中调用Hook
不要在普通的JavaScript函数中调用Hook。
Hook踩坑
在组件加载时发起异步请求
在组件加载时发起异步请求是很常见的,但是不可以这样写:
1 | useEffect(async () => { |
因为useEffect的第一个参数需要是一个函数,而async
函数返回的是一个Promise
,因此会报错。
我们可以在useEffect
里的函数写一个IIFE函数:
1 | useEffect(() => { |
又或者:
1 | useEffect(() => { |
设置State是异步的
1 | const [count, setCount] = useState(0) |
状态修改时异步的,所以马上读取count
,还是得到原始的值。如果在修改了State之后要马上使用它的值,可以在设置State的函数中返回一个函数,在函数中进行操作:
1 | const [count, setCount] = useState(0) |
又或者利用useEffect
做监听:
1 | function App() { |
当State为数组时
下面是一个例子:
1 | function App() { |
虽然控制台中打印出了新的函数,但是页面没有更新新的元素。这是因为React组件的更新机制对State只进行浅对比,因此我们在setArr
里传入原数组是不会更新页面的,我们需要传入一个新的数组:
1 | var arrAdd = () => { |