Hooks是一个将在React 16.7中引入的功能,它将改变我们在未来编写React应用的方式。
在Hooks出现之前,组件中的一些关键事情只能通过类组件来实现:拥有自己的状态,并使用生命周期事件。函数组件,更轻,更灵活,但功能有限。
钩子允许函数组件拥有状态并对生命周期事件做出响应,这使得类组件变得过时。它们还允许函数组件有一个处理事件的好方法。
访问状态
使用useState() API,你可以创建一个新的状态变量,并有办法改变它。useState() 接受状态项的初始值,并返回一个包含状态变量的数组,以及你为改变状态所调用的函数。由于它返回一个数组,我们使用数组析构来访问每个单独的项目,像这样。const [count, setCount] = useState(0)
这里有一个实际的例子。
import { useState } from 'react'
const Counter = () => {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
ReactDOM.render(<Counter />, document.getElementById('app'))
你可以添加你想要的任何数量的useState() 调用,来创建你想要的任何数量的状态变量。只要确保你在一个组件的顶层调用它(而不是在if 或任何其他块中)。
Codepen上的例子。
请看Flavio Copes(@flaviocopes)在CodePen上的PenReact Hooks例子#1计数器。
访问生命周期钩子
Hooks的另一个非常重要的特点是允许函数组件访问生命周期钩子。
使用类组件,你可以在componentDidMount 、componentWillUnmount 和componentDidUpdate 事件上注册一个函数,这些将服务于许多用例,从变量初始化到API调用到清理。
钩子提供了useEffect() 的API。该调用接受一个函数作为参数。
该函数在组件首次渲染时运行,并在随后的每次重新渲染/更新时运行。React首先更新DOM,然后调用任何传递给useEffect() 的函数。所有这些都不会阻塞UI的渲染,即使是在阻塞的代码上,不像以前的componentDidMount 和componentDidUpdate ,这使得我们的应用程序感觉更快。
例子。
const { useEffect, useState } = React
const CounterWithNameAndSideEffect = () => {
const [count, setCount] = useState(0)
const [name, setName] = useState('Flavio')
useEffect(() => {
console.log(`Hi ${name} you clicked ${count} times`)
})
return (
<div>
<p>
Hi {name} you clicked {count} times
</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<button onClick={() => setName(name === 'Flavio' ? 'Roger' : 'Flavio')}>
Change name
</button>
</div>
)
}
ReactDOM.render(
<CounterWithNameAndSideEffect />,
document.getElementById('app')
)
同样的componentWillUnmount 工作可以通过选择性地从我们的useEffect() 参数返回一个函数来实现。
useEffect(() => {
console.log(`Hi ${name} you clicked ${count} times`)
return () => {
console.log(`Unmounted`)
}
})
useEffect() 可以被多次调用,这对分离不相关的逻辑很有好处(这是困扰类组件生命周期事件的东西)。
由于useEffect() 函数是在每一个后续的重新渲染/更新中运行的,我们可以告诉React跳过一次运行,为了性能,可以添加第二个参数,这个参数是一个数组,包含要观察的状态变量列表。 只有当这个数组中的一个项目发生变化时,React才会重新运行副作用。
useEffect(
() => {
console.log(`Hi ${name} you clicked ${count} times`)
},
[name, count]
)
同样地,你可以通过传递一个空数组来告诉React只执行一次副作用(在加载时间)。
useEffect(() => {
console.log(`Component mounted`)
}, [])
useEffect() 这对于添加日志、访问第三方API等等都是非常好的。
Codepen上的例子。
请参阅CodePen上Flavio Copes(@flaviocopes)的PenReact Hooks例子#3副作用。
使用自定义钩子实现跨组件通信
编写你自己的钩子的能力是将大大改变你在未来编写React应用程序的方式的功能。
使用自定义钩子,你又多了一种在组件之间分享状态和逻辑的方式,为渲染道具和高阶组件的模式增加了一个重要的改进。这些模式仍然很好,但现在有了自定义钩子,在许多用例中就不那么相关了。
你如何创建一个自定义钩子?
钩子是一个函数,通常以use 开始。它可以接受任意数量的参数,并返回任何它想要的东西。
例子。
const useGetData() {
//...
return data
}
或
const useGetUser(username) {
//...const user = fetch(...)
//...const userData = ...
return [user, userData]
}
在你自己的组件中,你可以像这样使用钩子。
const MyComponent = () => {
const data = useGetData()
const [user, userData] = useGetUser('flavio')
//...
}
到底什么时候添加钩子而不是普通的函数,应该根据使用情况来决定,只有经验才知道。