简介
1 .useEffect就是class组件中的componentDidMount,componentDidUpdate,componentWillUnmount,他把三个合成了一个api
2 .React执行更新DOM之后,运行一些额外的代码,也就是说,所有的effect hook是在渲染dom之后执行
3 .分为需要清除的,还有不需要清除的。不需要清除的我们执行完毕之后就可以不管他了。比如拉取一些数据。需要清理的就需要专门清除这个函数,比如一开始开启了一个定时器,代码结束之后就需要清除这个定时器
4 .之前class模式我们把代码放到了componentDidMount里面也是这个意思,等界面渲染之后在执行副作用操作
5 .使用这个hook,可以告诉react组件需要在执行渲染后执行某些操作,react会保证传递的effect,在执行dom更新之后调用它。
6 .在函数内部调用useEffect,可以直接在内部访问定义的state变量
7 .默认情况下,在第一次渲染之后和每次更新之后都会执行,react保证每次运行effect的时候,dom都已经更新完毕
8 .effect中获得是最新的state值。
9 .实际上,传递给useEffect的函数在每次渲染中都会有所不同,不管是新的state,还是函数,其实每次重新渲染,都会生成新的effect,替换掉之前。
10 .某种意义上,effect更像是渲染结果的一部分,每个effect属于一次特定的渲染。每次重新渲染,都会生成新的effect
11 .使用useEffect调度effect不会阻塞浏览器更新屏幕,可以让应用响应更快。
实例代码
import React ,{useState , useEffect} from "react"
import axios from 'axios'
function Example(props){
const [name,setName]=useState(props.name)
const [time,setTime]=useState(new Date().toLocaleString())
// 当你需要在组件里面添加state,就可以使用hook了
// 1. 这里只是执行了初始化的state,如果props发生变化怎么操作。。
// effect:里面的函数一定会在dom渲染之后才执行
// 类似生命周期的函数
useEffect(() => {
console.log(name)
// 在这个里面执行副作用操作
// 数据获取,设置订阅,手动修改react组件的dom
}, [name]);
//这里如果不传任何东西,因为下面有个定时器,所以每次定时器执行,页面就会render,render完毕,这个还是会执行的
//如果传name,那么表示只有name发生变化,这个钩子才会被触发。
//同理,如果传入time的话,因为time是一直变化的,那么time每次变化的时候,相应的这个钩子也会被触发
// 需要清除的
useEffect(() => {
let timer=setInterval(()=>{
setTime(new Date().toLocaleString())
})
return () => {
clearInterval(timer)
};
});
// 不需要清除的,执行完毕之后就不需要在去管他了
// 我们想要在更新dom之后做一些额外的操作。发送一个网络请求.
useEffect(() => {
axios.get('http://127.0.0.1:5000/test').then((e)=>{
console.log(e)
setName(e.data)
})
});
return (
<>
<button onClick={()=>setName("nb")}>
click me{name}-{props.name}
</button>
<span>{time}</span>
</>
)
}
export default Example
注意事项
1 .useEffect会在每次组件render之后调用,相当于componentDidUpdate生命周期
2 .useEffect传第二个参数
1 .什么都不传,初始状态,每次render之后useEffect都会调用,相当于componentDidMount()和componentDidUpdate
useEffect(() => {
// 相当于 componentDidUpdate
document.title = count
})
2 .传入一个空数组,只会调用一次,相当于componentDidMount,componentWillUnmount
useEffect(() => {
// 相当于 componentDidMount
console.log('add resize event')
window.addEventListener('resize', onChange, false)
return () => {
// 相当于 componentWillUnmount
window.removeEventListener('resize', onChange, false)
}
}, [])
3 .传入一个数组,里面有一些变量,只有这些变量变化的时候才会执行useEffect
4 .每次重新渲染的时候,都会生成新的effect,替换掉之前的。某种意义上来讲effect像是渲染结果的一部分,每个effect都属于一次特殊的渲染
5 .根据传入的参数来决定他在什么执行这个渲染
6 .useEffect在执行副作用函数之前,会先调用上一次返回的函数,如果要清除副作用,useEffect应该返回一个清除作用的函数
跳过effect进行优化
1 .依赖项数组控制useEffect的执行
2 .如果某些特定值在两次重新渲染之间没有发生变化,可以通知react跳过对effect的调用,只要传递数组作为useEffect的第二个参数就可以
3 .如果想执行只运行一次的effect,仅仅是在组件挂载和卸载时执行,可以传递一个空数组([]),这就告诉react,你的effect不依赖于props或state中的任意值,所以他们永远都不需要重复执行
使用多个effect实现关注点分离
1 .使用hook的其中一个目的就是要解决class中生命周期函数经常包含不相关的逻辑,但是又把相关逻辑分离到了几个不同的方法之中的问题
2 .ocument.title 的逻辑是如何被分割到 componentDidMount 和 componentDidUpdate 中的,订阅逻辑又是如何被分割到 componentDidMount 和 componentWillUnmount 中的。而且 componentDidMount 中同时包含了两个不同功能的代码。这样会使得生命周期函数很混乱
3 .HOOK允许我们按照代码的用途来分离他们,而不是像生命周期函数那样,react将按照effect声明的顺序依次调用组件中的每一个effect
4 .在useEffect种调用函数的时候,应该把函数在useEffect中申明,不能放在外部定义,然后在useEffect中使用
1 .要记住effect外部函数使用了哪些props,state很难,所以通常可以在内部申明所需要的函数,然后就可以很容易的看出这个effect依赖了作用域中的哪些值
function Example({ someProp }) {
useEffect(() => {
function doSomething() {
console.log(someProp);
}
doSomething();
}, [someProp]); 安全(我们的 effect 仅用到了 `someProp`)
}
2 .只有当函数不引用props,state以及由他衍生而来的值时,你才能放心的把他从依赖列表中删除
5 .如果
6 .useEffect的不作为componentDidUnmount的话,传入第二个参数时一定注意:第二个参数不能为引用类型,引用类型比较不出来数据的变化,会造成死循环
基本类型和引用类型
1 .基本类型:str,num,bool,null,undefined
2 .引用类型:[],{},数组和对象
3 .区别
1 .基本类型的值是一经确定就不能改变的
2 .基本类型的比较是值的比较,只有在值相等的时候才会相等
3 .基本类型的变量是存放在栈区的
4 .引用类型的值是可以变化的
5 .引用类型的值是同时保存在堆和栈内存中的对象,栈中保存变量标识符和指向堆内存中该对象的指针
6 .引用类型的比较是引用的比较,也就是引用类型每次比较都是不同的。会一直返回false
effect
1 .react 会在组件卸载的时候执行清除操作
2 .effect是在每次渲染的时候都会执行,首先执行当前上一个effect进行清除,然后才执行本次的effect内容
3 .可以按照用途来定义多个Hook,react将按照effect声明的顺序每一次调用每一个effect
4 .每次更新的时候都会运行effect,首先清除上一次的残留effect,然后运行本次的effect
// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // 运行第一个 effect
// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // 清除上一个 effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // 运行下一个 effect
// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // 清除上一个 effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // 运行下一个 effect
// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // 清除最后一个 effect
传入值
1 .如果你传入了一个空数组,effect内部的props和state就会一直拥有其初始值,
困惑问题
1 .有时候会出现无限重复请求
1 .effect里面修改了外面的stete,但是没有添加依赖参数,导致每次state改变导致重新渲染,然后这个又被执行。
2 .或者每次effect里面修改的state就是你依赖的state,也是这个感觉
3 .感觉hook就是我心中最合适的渲染机制
4 .
继续补习
1 .发现之前看过一遍的东西,很长时间之后在看还是能发现有用的东西
2 .它仅仅只是在渲染输出中插入了count这个数字。这个数字由React提供。当setCount的时候,React会带着一个不同的count值再次调用组件。然后,React会更新DOM以保持和渲染输出一致。这里关键的点在于任意一次渲染中的count常量都不会随着时间改变。渲染输出会变是因为我们的组件被一次次调用,而每一次调用引起的渲染中,它包含的count值独立于其他渲染。
3 .每一次渲染都有它自己的事件处理函数。我们的组件函数每次渲染都会被调用,但是每一次调用中count值都是常量,并且它被赋予了当前渲染中的状态值。只要是简单的模式还能看的出来,要是那些比较复杂的可能就忘了这个东西
4 .并不是count的值在“不变”的effect中发生了改变,而是effect 函数本身在每一次渲染中都不相同
5 .每一个effect版本“看到”的count值都来自于它属于的那次渲染,如果不加依赖参数的话,那就是一个普通函数的操作,每次都会渲染,都会拿到最新值
6 .React会记住你提供的effect函数,并且会在每次更改作用于DOM并让浏览器绘制屏幕后去调用它
7 .所以虽然我们说的是一个 effect(这里指更新document的title),但其实每次渲染都是一个不同的函数 — 并且每个effect函数“看到”的props和state都来自于它属于的那次特定渲染
8 .概念上,你可以想象effects是渲染结果的一部分
9 .实际操作
React: 给我状态为 0时候的UI。
你的组件:
给你需要渲染的内容: <p>You clicked 0 times</p>。
记得在渲染完了之后调用这个effect: () => { document.title = 'You clicked 0 times' }。
React: 没问题。开始更新UI,喂浏览器,我要给DOM添加一些东西。
浏览器: 酷,我已经把它绘制到屏幕上了。
React: 好的, 我现在开始运行给我的effect
运行 () => { document.title = 'You clicked 0 times' }。
现在我们回顾一下我们点击之后发生了什么:
你的组件: 喂 React, 把我的状态设置为1。
React: 给我状态为 1时候的UI。
你的组件:
给你需要渲染的内容: <p>You clicked 1 times</p>。
记得在渲染完了之后调用这个effect: () => { document.title = 'You clicked 1 times' }。
React: 没问题。开始更新UI,喂浏览器,我修改了DOM。
Browser: 酷,我已经将更改绘制到屏幕上了。
React: 好的, 我现在开始运行属于这次渲染的effect
运行 () => { document.title = 'You clicked 1 times' }。
网友评论