写在前面
??在React中,有两种组件:函数组件与class组件。两者的区别之一就在于:class组件有生命周期,而函数组件由于没有继承React.component,所以函数组件无生命周期。于是你查阅文档,发现使用useEffect可以解决这个问题。那么问题就来了:
- 如何用useEffect模拟componentDidMount生命周期
- 如何正确地在useEffect里请求数据?
- []又是什么?
- 为什么有时候出现无限重复请求的问题?
- …
??刚开始使用这个hooks的时候,我也同样被这些问题所困扰,那么这篇文章就会深入讲解帮你明白上面问题。
在开始前
??如果你不想完整阅读这篇文章,急于求成,可以快速浏览这部分。要是对于某个地方不太理解,在针对某一部分进行深入学习。
Question1:如何用useEffect模拟componentDidMount生命周期?
??对于这个问题,我们习惯性的会想到useEffect(()=>{fectchData()},[]),将依赖数组置为空。虽然这是一种管用的方法,但它和componentDidMount不一样,effect会捕获props和state。所以即便在回调函数中,你拿到的还是初始的props和state。如果你想得到"最新"的值,你可以使用ref。不过这通常有更简单的方式实现,所以你并不一定要用ref.
Question2:如何正确地在useEffect里请求数据?
??每个useEffect都是不同的,随着状态的变化,一个个effect出生,一个个effect 死去,怎么保证得到的effect是我们想要的呢,这就需要借助依赖数组,在请求过程中将所有遗传因子写在依赖数组中,当然这是最保险的方法。 Question3:[]又是什么?
??[]表示effect没有使用任何React数据流里的值,因此该effect仅被调用一次是安全的。[]同样也是一类常见问题的来源,也即你以为没使用数据流里的值但其实使用了。你需要学习一些策略(主要是useReducer 和 useCallback)来移除这些effect依赖,而不是错误地忽略它们。
Question4:为什么有时候出现无限重复请求的问题?
??这个问题通常发生于你在effect里做数据请求并且没有设置effect依赖参数的情况。没有设置依赖,effect会在每次渲染后执行一次,当effect更新时会导致更新并再次触发effect。无限循环的发生也可能是因为你设置的依赖总是改变,但移除依赖或盲目使用[]通常不是一种很好的解决方式。你要做的应该是从根本上解决问题。
浅谈useEffect
??useEffect可以让你在函数中执行副作用操作。那么,什么是副作用呢?简单来说就是用于处理组件中的effect,通常用于请求数据,事件处理,数据订阅,以及手动更新React中的DOM等相关操作。由于我们渲染的页面都是静态的,任何在其之后的操作都会对此产生影响,所以称之为副作用。副作用主要分为两种:
小栗子:
??给鼠标的click事件添加了一个监听器,当鼠标每次点击时都获取改变p标签中的值,并在控制台打印出 inner。代码如下: ??执行之后我们会发现,打印inner的次数并不是点一次打印一次,而是点击一次,inner被打印了许多次。这是因为我们在每次更新渲染结束后都会调用useEffect函数,这样只会添加越来越多的click事件。
?? 解决办法:由于useEffect通过return进行清除操作,因此我们只需要return取消click事件的函数即可清除,代码如下: ??可以发现点击鼠标后,打印inner的次数是线性增加的,即点一次inner就会被打印一次,而不是被打印多次。
useEffect的第二个参数?
第一个参数为一个函数
??为了告诉React组件需要在渲染后执行哪些操作,这个函数会在DOM更新之后被调用,默认情况下useEffect在每次渲染之后执行,但也可以手动控制它执行。
第二个参数
- 没有第二个参数:每次渲染都会触发这个副作用
- 第二个参数为一个数组,当这个数组中任意一个值变化时,useEffect就会被重新执行。主要有两种情况:
??数组中无参即空数组,用于告诉useEffect不依赖于任何props、state中的值,useEffect执行一次之后就不在执行 ??数组中有参,用于告诉useEffect依赖于数组中的变量值,即每当变量变化时调用
useEffect的最佳实践
useEffect与生命周期函数有区别吗?
1.useEffect
let [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
console.log("count的值为",count)
},3000)
}, [count])
function handleClick() {
setCount(count + 1);
}
return(
<button onClick={this.handleClick.bind(this)}>点我更新</button>
)
2.componentDidUpdate
constructor(props) {
super(props);
this.state = {
count: 0
}
}
componentDidUpdate() {
setTimeout(() => {
console.log("count的值为",this.state.count)
},3000)
}
handleClick() {
const { count } = this.state;
this.setState({ count: count + 1 });
}
render() {
return (
<button onClick={this.handleClick.bind(this)}>点我更新</button>
)
}
??可以发现,useEffect会依赖数组中的变量,即将每次count变化的数据都打印出来,而使用componentDidUpdate打印了最后一个数据很多次,主要是因为class里面的state随着render发生变化而变化的,而useEffect里面的所有东西都是每次render独立的。
如何定义函数与请求?
- 某函数只在effect中使用的话,那就在effect中定义
- 某函数在多个地方用到,建议独立定义,使用useCallBack包裹起来并且在依赖数组里把依赖项写
- useEffect回调函数不建议用async修饰,可以在回调函数中再写一个async函数进行调用
useEffect(async()=>{
const res = await fetchNewData(id)
setData(res.data)
},[id])
useEffect(()=>{
const fetchData = async() => {
const res = await fetchNewData(id)
setData(res.data)
}
fetchData()
},[id])
在结束前
??现在你应该差不多了解到关于如何使用useEffect的知识,如果我有遗漏的地方或有什么意见或建议希望大家在评论区多多交流,我很期待在评论区看到您的想法,谢谢阅读😘
|