- 作者:老汪软件技巧
- 发表时间:2024-11-02 17:01
- 浏览量:
react的函数组件里面没有生命周期,也没有state,没有state 可以用useState来替代。
useEffect可以看作是componentDidMount、componentDidUpdate、componentWillUnmounrt三个生命周期的组合,可以覆盖这些生命周期函数组合起来使用的所有场景。useEffect是定义副作用的,可以在函数组件中执行一些副作用操作。
执行时机
useEffect可以看做三个生命周期函数的组合,也就是在以下时候会执行
componentDidMount(组件已经挂载)
componentDidUpdate(组件已经更新)
componentWillUnmount(组件即将卸载)
副作用
函数的副作用就是函数中除了操作函数内部的变量执行业务还操作了函数外部变量来执行业务。
react项目中,一般副作用操作有:
发送ajax请求调用浏览器的API(比如启动定时器、浏览器的存储信息localStorage.getItem()等)手动更改真实DOMuseEffect使用
useEffect()有两个参数:第一个参数是要执行的回调函数也就是需要执行的操作,它是必填参数,第二个参数是一个依赖项数组,根据数组里的变量是否变化决定是否执行函数(根据需求第二个参数决定是否填写,它是可选的)
import {useEffect} from "react"
useEffect(effect,dependencies)
useEffect根据第二个参数的传参有不同的作用:
useEffect的第一个参数Effect是要执行的副作用函数,它可以是任意的自定义函数,可以在这个函数里面操作一些浏览器的API或者和外部环境进行交互比如网络请求等。这个函数会在每次组件渲染完成之后被调用,组件每渲染一次该函数就自动执行一次。在组件的页面首次DOM 加载后,副作用函数也会执行。
useEffect的第二个参数dependencies用于限制该副作用的执行条件,使用一个数组指定副作用函数的依赖项,只有依赖项发生变化,才会重新渲染。如果第二个参数是一个空数组,就表明副效应参数没有任何依赖项。因此,副作用函数这时只会在组件首次加载完成后执行一次,后面组件重新渲染,就不会再次执行。
不传第二个参数举例说明
import { useEffect,useState } from 'react'
function App() {
let [count,setCount] = useState(0);
const addHandle = () => {
let num = count++;
setCount(num);
};
// 第一个参数是一个回调函数,必填项
useEffect(() => {
console.log("effect")
});
return (
<>
<h1>Apph1>
<p>{count}p>
<button onClick={addHandle}>addbutton>
>
)
}
export default App
控制台第一次输出表示在组件挂载后执行了useEffect的回调函数,相当于componentDidMount的作用。之后点击按钮修改了数据,在组件更新后useEffect的回调函数再次触发,相当于componentDidUpdate的作用。
第二个参数传入空数组举例说明
import { useEffect,useState } from 'react'
function App() {
let [count,setCount] = useState(0);
const addHandle = () => {
let num = count++;
setCount(num);
};
// 第一个参数是一个回调函数,必填项
useEffect(() => {
console.log("effect")
},[]);
return (
<>
<h1>Apph1>
<p>{count}p>
<button onClick={addHandle}>addbutton>
>
)
}
export default App
控制台第一次输出表示在组件挂载后执行了useEffect的回调函数,相当于componentDidMount的作用。之后点击按钮修改了数据,在组件更新后useEffect的回调函数没有再次执行。
import {useState,useEffect} from 'react'
export default function App() {
let [objarr,setObjarr]=useState([]);
useEffect(()=>{
console.log('App组件对应的模板在页面中加载完成时调用');
fetch('http://127.0.0.1:7001/test')
.then(res=>res.json())
.then(data=>{
console.log(data,111);
setObjarr(data)
});
});
return (
<div>
{
objarr.map((el,index)=>{
return(
<div key={index}>
<p>{el.title}p>
<p>{el.author}p>
div>
)
})
}
<p>App组件p>
div>
)
}
在组件第一次加载时网络请求,没有设置依赖项为空数组,就会一直发起网络请求。setObjarr(data)每一次都会得到一个新的引用数据然后刷新页面,就会一直发起网络请求,如果后端发送的数据每次都是不同的就会一直刷新页面就容易造成项目进入死循环。
所以需要设置第二个参数是一个空数组,这样副作用函数只会在组件首次加载后执行一次,后面组件重新渲染,就不会再次执行
import {useState,useEffect} from 'react'
export default function App() {
let [objarr,setObjarr]=useState([]);
useEffect(()=>{
console.log('App组件对应的模板在页面中加载完成时调用');
fetch('http://127.0.0.1:7001/test')
.then(res=>res.json())
.then(data=>{
console.log(data,111);
setObjarr(data)
});
},[]);
return (
<div>
{
objarr.map((el,index)=>{
return(
<div key={index}>
<p>{el.title}p>
<p>{el.author}p>
div>
)
})
}
<p>App组件p>
div>
)
}
第二个参数数组中存放state数据
import { useEffect,useState } from 'react'
function App() {
let [count,setCount] = useState(0);
let [msg,setMsg] = useState("hello");
const addCount = () => {
setCount(count++);
};
const changeHandle = () => {
setMsg("hello world");
};
// 第一个参数是一个回调函数,必填项
useEffect(() => {
console.log("effect")
},[msg]);
return (
<>
<h1>Apph1>
<p>{msg}p>
<p>{count}p>
<button onClick={addCount}>addbutton>
<button onClick={changeHandle}>changebutton>
>
)
}
export default App
控制台第一次输出表示在组件挂载后执行了useEffect的回调函数,相当于componentDidMount的作用。之后修改了监听的state数据,在组件更新后useEffect的回调函数再次执行。而修改了其他数据,此时useEffect的回调函数也不会执行。
此时和vue的watch监听是有区别的,vue的watch监听默认是一开始不会执行回调函数的,而使用useEffect来监听某个数据,开始就会执行一次回调函数,类似于componentDidMount的作用。
当然数组里面也可以写多个([数据1,数据2]),同时监听多个数据的变化。
import { useEffect,useState } from 'react'
function App() {
let [count,setCount] = useState(0);
let [msg,setMsg] = useState("hello");
const addCount = () => {
setCount(count++);
};
const changeHandle = () => {
setMsg("hello world");
};
// 第一个参数是一个回调函数,必填项
useEffect(() => {
console.log("effect");
console.log(count,msg)
},[msg,count]);
return (
<>
<h1>Apph1>
<p>{msg}p>
<p>{count}p>
<button onClick={addCount}>addbutton>
<button onClick={changeHandle}>changebutton>
>
)
}
export default App
清除页面的副作用
副作用是随着组件加载而发生的,那么组件卸载时,可能需要清理这些副效应。 useEffect()允许返回一个函数,在组件卸载时清理副作用。如果不需要清理副作用,useEffect()就不用返回任何值。
import { useEffect } from 'react'
export default function Des() {
useEffect(() => {
setInterval(() => {
console.log('Des组件对应的模板在页面中加载完成时调用');
}, 1000);
});
return (
<div>Des组件div>
)
}
import {useState} from 'react'
import Des from './Des.jsx'
export default function App() {
let [flag,setFlag]=useState(true);
let change=()=>{
setFlag(false)
}
return (
<div>
<p>App组件p>
{flag&&<Des>Des>}
{/* flag为true时,会执行后面的表达式,逻辑与表达式的值就是后面表达式的返回值显示Des组件 */}
{/* flag为false时,不会执行后面的表达式,不会显示Des组件 */}
<button onClick={change}>销毁Des组件button>
div>
)
}
Des组件被销毁了,但是组件中的副作用依然存在,所以需要在组件卸载时清理副作用。
import { useEffect } from 'react'
export default function Des() {
useEffect(() => {
let timer=setInterval(() => {
console.log('Des组件对应的模板在页面中加载完成时调用');
}, 1000);
return ()=>{
clearInterval(timer);
}
});
return (
<div>Des组件div>
)
}
import {useState} from 'react'
import Des from './Des.jsx'
export default function App() {
let [flag,setFlag]=useState(true);
let change=()=>{
setFlag(false)
}
return (
<div>
<p>App组件p>
{flag&&<Des>Des>}
{/* flag为true时,会执行后面的表达式,逻辑与表达式的值就是后面表达式的返回值显示Des组件 */}
{/* flag为false时,不会执行后面的表达式,不会显示Des组件 */}
<button onClick={change}>销毁Des组件button>
div>
)
}
Des组件在销毁时,返回的函数被执行了,组件中的副作用就被清除了。
总结