React Hooks的简介

52 阅读4分钟

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的另一个非常重要的特点是允许函数组件访问生命周期钩子。

使用类组件,你可以在componentDidMountcomponentWillUnmountcomponentDidUpdate 事件上注册一个函数,这些将服务于许多用例,从变量初始化到API调用到清理。

钩子提供了useEffect() 的API。该调用接受一个函数作为参数。

该函数在组件首次渲染时运行,并在随后的每次重新渲染/更新时运行。React首先更新DOM,然后调用任何传递给useEffect() 的函数。所有这些都不会阻塞UI的渲染,即使是在阻塞的代码上,不像以前的componentDidMountcomponentDidUpdate ,这使得我们的应用程序感觉更快。

例子。

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')
  //...
}

到底什么时候添加钩子而不是普通的函数,应该根据使用情况来决定,只有经验才知道。