1. 什么是React Hook中的useEffect函数?
在React的函数式组件中,如果我们需要实现类似于componentDidMount和componentDidUpdate的生命周期函数的效果,那么就可以使用React Hook中的useEffect函数。
useEffect的作用是:在函数组件中执行副作用操作,如获取数据,设置订阅,以及手动更改React组件中的DOM元素。
useEffect函数接受两个参数,第一个参数是一个函数,表示需要执行的副作用操作;第二个参数是一个数组,表示需要跟踪的变量列表。useEffect函数会在组件渲染完成后执行第一个参数所表示的函数(又称为effect函数),如果指定了第二个参数,则只有在数组中的变量发生了变化,才会重新执行effect函数,否则effect函数不会再次执行。
2. useEffect函数的参数详解
2.1 第一个参数
useEffect函数的第一个参数是一个函数,可以执行任何需要的操作。这个函数有两种形式:
// 第一种形式:不带返回值的函数
function useEffect(effect: EffectCallback, deps?: DependencyList): void;
// 第二种形式:带返回值的函数
function useEffect(effect: EffectCallback, deps?: DependencyList): (() => void) | void;
第一种形式的函数不需要返回值,只需要执行想要的操作,如获取数据,更新组件等。
第二种形式的函数需要带返回值,返回一个清除函数,用于清除effect的副作用。在组件卸载时,React会自动执行这个清除函数。
2.2 第二个参数
useEffect函数的第二个参数是一个数组,用于指定useEffect的依赖项。只有当数组中的依赖项发生了变化时,useEffect才会重新执行。如果不指定第二个参数,默认为[],即只在组件首次渲染时执行effect函数。
useEffect函数的第二个参数的类型为DependencyList,它是一个数组类型。数组中存放着effect函数需要依赖的变量,当这些变量的值发生变化时,effect函数才会重新执行。
需要注意的是,依赖项的内容一定要是完整的(如果是对象,则每个属性都要列举出来),否则会导致effect不执行或者effect不会预期地执行。
3. useEffect函数的常见用法
3.1 使用useEffect获取数据
在React中,我们经常需要从服务器获取数据并且显示在组件中。借助useEffect函数,我们可以在组件渲染完成后获取数据,然后再更新组件。
示例代码如下:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(res => setUsers(res.data))
.catch(err => console.log(err));
}, []);
return (
<div>
<h1>Users:</h1>
<ul>
{users.map(user => (
<li key={user.id}>
{user.name}
</li>
))}
</ul>
</div>
);
}
export default App;
代码中,我们使用了useEffect来获取数据,并使用useState来保存数据。getAllUsers函数用于获取用户数据,这个函数会在组件渲染完成后自动执行,因为在useEffect的第二个参数中传递了一个空数组,表示只需要在组件首次渲染时执行这个函数。
3.2 使用useEffect更新DOM元素
除了获取数据之外,使用useEffect还可以用来手动更新DOM元素,这个场景在组件需要根据用户的操作来改变dom结构时尤为适用。我们可以在effect函数中,手动操作DOM元素,然后更新组件。
示例代码如下:
import React, { useState, useEffect, useRef } from 'react';
function App() {
const [count, setCount] = useState(0);
const countRef = useRef();
useEffect(() => {
countRef.current.innerHTML = count;
}, [count]);
return (
<div>
<h1>Count: <span ref={countRef}></span></h1>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
);
}
export default App;
代码中,我们使用了useEffect函数来手动更新DOM元素。当count变量的值发生变化时,effect函数会重新执行,然后将count的值更新到DOM元素中。
3.3 使用useEffect实现类似于componentDidMount和componentDidUpdate的效果
在React中,我们经常需要在组件挂载后执行一些操作,如设置订阅,更新DOM元素等,这个场景下,可以使用useEffect来实现类似于componentDidMount的效果。同时,还可以使用useEffect来监听组件中某个变量的变化,然后进行相关操作,这个场景下,可以使用useEffect来实现类似于componentDidUpdate的效果。
示例代码如下:
import React, { useState, useEffect } from 'react';
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = 'Count: ' + count;
}, [count]);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
);
}
export default App;
代码中,我们使用了useEffect函数来监听count变量的变化。当count变量的值发生变化时,effect函数会重新执行,然后更新document.title的值。
4. useEffect函数的常见问题
在使用useEffect函数时,我们需要注意一些问题,否则可能会导致效果出现一些意外情况。
4.1 必须在函数组件的顶层使用
useEffect函数必须在函数组件的顶层使用,而不能在嵌套的函数或条件语句中使用。这是因为React需要保证每次渲染时都能执行effect函数,如果useEffect被放在了条件语句中,则可能只有在特定条件下才会执行effect函数,这样可能会导致程序出现一些异常。
4.2 useEffect的执行不保证同步
React在运行useEffect函数时,并不保证其同步执行,也就是说,useEffect函数的执行可能会先于组件渲染完成,或者在组件渲染完成之后。因此,我们在使用useEffect函数时,需要避免依赖更新后立即执行的操作,如获取DOM节点的尺寸,因为此时DOM节点可能还未被渲染完成。
4.3 清除副作用的代码应该在effect函数内部定义
在使用useEffect函数时,如果需要清理副作用操作,在代码中最好将清除副作用的代码定义在effect函数内部,这样有助于组织逻辑和避免出现一些代码错误。
5. 总结
useEffect函数是React提供的一个非常强大的工具,它可以帮助我们在函数式组件中实现componentDidMount和componentDidUpdate等生命周期函数的功能,同时在需要时可以更新DOM元素,或者获取数据。使用useEffect函数时,我们需要注意函数的参数和使用方式,避免出现一些错误情况。