使用 React hooks 重构组件
前言
React 16.8 引入了钩子;一种在 React 中处理效果和状态的新方法。钩子让我们将组件编写为函数,并且仍然具有基于类的组件的所有功能。
我将分享一下我第一次使用它们进行重构的经历。希望对你有帮助!
我们正在使用的类组件。
我们要重构的组件将 id
作为 prop,并向 API 发出请求以获取具有给定 ID 的用户的数据。它的 id
prop 可以随时更改,因此如果 ID 更改,我们也必须重新获取用户数据。因此,我们有 componentDidMount
和 componentDidUpdate
来处理第一次渲染和任何后续的 prop 更改。此示例中的 render
只是转储用户数据。
import React, { Component } from 'react'
export default class Demo extends Component {
constructor(props) {
super(props)
this.state = {
user: undefined,
}
}
componentDidMount() {
fetchUser(this.props.id).then(user => this.setState({ user }))
}
componentDidUpdate(prevProps) {
if (this.props.id !== prevProps.id) {
fetchUser(this.props.id).then(user => this.setState({ user }))
}
}
render() {
return (
<pre>
<code>{JSON.stringify(this.state.user, null, 4)}</code>
</pre>
)
}
}
不要在意
fetchUser
的最终执行的结果 -fetch
只是一个接口请求的API。
重构为hooks
我们将使用两个钩子:
-
useState
, 在组件中保存一个状态。我们将使用它来保存我们从 API 获取的user
数据。 -
useEffect
.这让我们可以在我们的组件中运行副作用。也就是说,类似于我们类组件的 React 生命周期方法上:
import React, { useState, useEffect } from 'react'
const DemoWithHooks = props => {
const [user, setUser] = useState(undefined)
useEffect(() => {
// TODO
})
return (
<pre>
<code>{JSON.stringify(user, null, 4)}</code>
</pre>
)
}
当我们调用 useState
时,我们得到一个包含两项的数组。第一项是状态的实际值,第二项是用于更新该值的函数。
传递给 useState
的值是初始值。在这里,我们传入了 undefined
,所以很明显当这个组件运行时我们还没有用户数据。这时候来看一下 useEffect
挂钩。
useEffect
useEffect
获取一个函数并在组件运行时执行它。这意味着它会在组件首次挂载时和组件重新渲染时运行。
让我们用一个获取用户并更新状态的函数来
useEffect(() => {
fetchUser(props.id).then(setUser)
})
它将在以下两种情况运行
- 当组件第一次渲染时
- 组件渲染后
prop 的 id
每次该属性发生变化时,我们都能再次获取用户的数据。
但是,如果这个组件有很多属性,或者有其他的,只要这些 props 中的任何一个发生变化,并且组件再次渲染,我们的 fetchUser
代码就会运行。
即使 props.id
没有更改,它也会这样做,如果我们已经拥有该用户的数据,那只是一个浪费的网络请求。
在类的组件中,我们将通过在 componentDidUpdate
代码中添加条件来解决这个问题:
componentDidUpdate(prevProps) {
if (this.props.id !== prevProps.id) {
fetchUser(this.props.id).then(user => this.setState({ user }))
}
}
这确保我们只在我们关心的数据发生变化时才发出网络请求。
这里我们可以通过传递第二个参数来对 useEffect
限制,该参数是必须更改后才会重新运行useEffect
useEffect(
() => {
fetchUser(props.id).then(setUser)
},
[props.id]
)
现在我们的效果将在第一次渲染时运行,并且也会在 props.id
更改时运行。如果任何其他数据发生变化,则不会触发该效果。
最后的样子
const DemoWithHooks = props => {
const [user, setUser] = useState(undefined)
useEffect(
() => {
fetchUser(props.id).then(setUser)
},
[props.id]
)
return (
<pre>
<code>{JSON.stringify(user, null, 4)}</code>
</pre>
)
}
如果将上面的代码与最开始的进行比较,我认为它更简洁。最开始组件在 componentDidMount
和 componentDidUpdate
中有一些几乎重复的代码,由于 useEffect
允许我们在一个函数中表达所有内容,因此将其完全删除。我们也避免了 componentDidUpdate
中 props 的尴尬比较。 useEffect
让我们可以非常简洁地定义效果以及导致它重新运行的原因。
如果您正在使用钩子,我还推荐 eslint-plugin-react-hooks 包,它会为您提供方便的 linter 错误或使用钩子时一些常见错误的警告。我一直发现它特别适用于捕捉我在调整以在基于类的组件上使用挂钩时出现的轻微错误。
全文完
谢谢!
转载自:https://juejin.cn/post/7201677717208088631