1. 前言
https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/
React Hooks 是 React 16.8 引入的新特性,允许我们在不使用 Class 的前提下使用 state 和其他特性。React Hooks 要解决的问题是状态共享,是继 render-props 和 higher-order components 之后的第三种状态逻辑复用方案,不会产生 JSX 嵌套地狱问题。
React Hooks 的设计目的,就是加强版函数组件,完全不使用”类”,就能写出一个全功能的组件。
Hook 这个单词的意思是”钩子“。 React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码”钩”进来。 React Hooks 就是那些钩子。
2. useState
2.1 useState介绍
useState用于为函数组件声明状态变量(state), 纯函数不能有状态, 所以把它放在钩子里面。
const [count, setCount] = useState(0)
该函数接收的参数是状态的初始值(Inital State), 上例的初始值就是0。 该函数返回一个数组, 数组的第一个元素式一个变量(count), 指向状态的当前值, 第二个元素式一个函数, 用来更新状态, 约定式set前缀加上状态的变量名(setCount)
2.2. setXxx()的2种写法:
setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
import React, { useState } from 'react'
export default function State(){
const [count, setCount] = useState(0)
const add = () => {
setCount(count+1)
}
return <div>
<h2>当前的值: {count}</h2>
<button onClick={add}>加1</button>
</div>
}
setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
import React, { useState } from 'react'
export default function State(){
const [count, setCount] = useState(0)
const add= () => {
setName( count=> count+ 1)
}
return <div>
<h2>当前的值: {count}</h2>
<button onClick={add}>加1</button>
</div>
}
2.3 不能使用判断语句
```javascript import React, { useState } from ‘react’;
let showSex = true function Example2(){ const [ age , setAge ] = useState(18) if(showSex){ const [ age , setAge ] = useState(18) showSex=false } return (
张三今年:{age}岁
**useState**不能在**if...else...**这样的条件中进行调用, 必须按照相同的顺序进行渲染。
<a name="bVdkh"></a>
## 2.4 useState和setState的区别:
**不同点:**
- **useState** 初始**state**的时候可以是**任意的数据类型**,**setState**初始**state**的时候**state**只能是**对象**;
- **setState**是对状态的**合并**, 但是**useState**是对状态进行**替换**;只不过内部有个**防抖**的优化才导致组件不会立即被重新渲染
- 使用**useState**改变状态时, 使用**Object.is**算法比较状态的前后值,如果前后状态相同, 那么组件不会重新渲染。
```javascript
const [count, setCount] = useState(0);
function update(){
setCount(0) // 不会重新渲染
}
//
function update(){
setCount() // 会重新渲染
}
但是class组件的setState,即使状态的前后时相同的,也会进行渲染。
state = {
count:0
}
update = () => {
this.setState({
count:0 //会渲染
})
}
update = () => {
this.setState({}) //会渲染
}
相同点:
setState,useState拿到最新的state后,重新渲染页面,页面展示最新的数据状态。
总结: React Hooks不能出现在条件判断语句中, 因为它必须有完全一样的渲染顺序。
3. useEffect()
3.1 useEffect的介绍
Effect Hook可以让你在函数组件中执行副作用操作,最常见的就是向服务器请求数据。以前放在componnetDidMount里面的代码,现在可以放在useEffect()。
当你调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数
useEffect的用法如下:
useEffect(() => {
// Async Action
}, [dependencies])
可以把useEffect Hook看作是componentDidMount、componentDidUpdate和componentWillUnmount这三个函数的组合。
useEffect本身是一个函数,由React框架提供,在函数组件内部调用即可。
import React, { useEffect } from 'react';
function Welcome(props) {
useEffect(() => {
document.title = '加载完成';
});
return <h1>Hello, {props.name}</h1>;
}
useEffect的第一个参数是一个函数,它就是所要完成的副效应。组件加载以后,React就会执行这个函数。
3.2 useEffect到底做了什么?
通过使用useEffect, 可以告诉组件需要在渲染后执行哪些操作。 React会保存你传递的函数, 并且在执行DOM更新之后在调用它。
3.3 为什么在组件内部调用useEffect?
将useEffect放在组件内部,让我们可以在effect中直接访问state变量, 不需要特殊的API来读取它, 因为它已经保存在函数作用域中了。
3.4 useEffect会在每次渲染后都执行吗?
默认情况下, 它在第一次渲染之后或者每次更新之后都会执行。
3.6 为什么要在effect中返回一个函数
这个effect有可选的清除机制。 每个effect都可以返回一个清除函数, 可以将添加的定时器和订阅的逻辑放在一起,这个新的函数将会在组件下一次重新渲染之后执行。
useEffect(() => {
const timer = setTimeout(() => {
setCount(count+1)
console.log(123);
},1000)
return () => {
clearInterval(timer)
}
},[name])
3.7 useEffect的第二个参数
3.7.1 第二个参数为[]
- 如果依赖数组为空数组,那么回调函数会在第一次渲染结束后(componentDidMount)执行,返回的函数会在组件卸载时(componentWillUnmount)执行。
3.7.2 第二个参数为空useEffect(()=>{
const users = 获取全国人民的信息()
},[])
如果不传依赖数组,那么回调函数会在每一次渲染结束后(componentDidMount 和 componentDidUpdate)执行。
3.7.3 第二个参数为[name]useEffect(()=>{
const users = 获取全国人民的信息()
})
useEffect最后加[],并且[]里面加的字段就表示,这个字段更改了,我这个effect才执行
3.7.4 第二个参数为[name, age]useEffect(() => {
console.log('我变了');
},[count])
如果我想要分别name和age呢:可以多写几个
3.7.5 取消订阅useEffect(() => {
console.log('我变了');
},[count,age])
如果我们之前订阅了什么,最后在willUnMount这个生命周期里面要取消订阅,在effect的return里面可以做取消订阅的事useEffect(() => {
const timer = setTimeout(() => {
setCount(count+1)
console.log(123);
},1000)
return () => {
clearInterval(timer)
}
},[name])
3.8 useEffect()的用途:
- 获取数据(data fetching)
- 事件监听或订阅
- 改变DOM
- 输出日志
3.9 useEffect中如何使用async await
```javascript //1. 使用自执行函数 useEffect(()=>{ // 使用自执行函数 IIFE (async function fn(){
})()await otherFn();
},[])
//2. 在useEffect的回调参数内部定义一个async函数: useEffect(()=>{ const fn=async ()=>{ // do something await otherFn() } fn() },[])
// 3. 在useEffect外部定义async函数,在回调参数中去执行: const fn=async ()=>{ // do something await otherFn() } useEffect(()=>{ fn() },[])
<a name="I0cCl"></a>
# 4. useLayoutEffect
```javascript
import React,{useState, useEffect, useLayoutEffect, useRef}from 'react'
export default function App() {
const [n, setN] = useState(0)
const time = useRef(null)
const onClick = () => {
setN(i => i + 1)
time.current = performance.now()
}
useEffect(() => {
if(time.current){
console.log('useEffect时序')
console.log(performance.now()-time.current)
}
})
useLayoutEffect(() => {
if(time.current){
console.log('useLayoutEffect时序')
console.log(performance.now()-time.current)
}
})
return (
<div className="App">
n:{n}
<button onClick={onClick}>+1</button>
</div>
);
}
5. useRef()
useRef是一个对象,它有一个current属性, 不管函数组件执行多少次, 而useRef()返回的永远都是原来的那个。
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样
const myRef = useRef();
const show = () => {
console.log(myRef.current.value);
}
<input type="text" placeholder='请输入值' ref={myRef} />
<button onClick={show}>显示输入框内的值</button>
5.1 useRef的特点:
- useRef是一个只能在函数组件使用的方法
2. useRef是除字符串ref、函数ref、createRef之外的第四种获取ref的方法
3. useRef在渲染周期内永远不变, 因此可以用来引用某些数据
4. 修改ref.current不会引发组件重新渲染5.2 useRef VScreateRef
- 两者都是获取ref的方式, 都有一个current属性
2. useRef只能用于函数组件, createRef可以用于类组件中
3. useRef在每次重新渲染后都保持不变, 而createRef每次都会发生变化。6. useContext ——共享状态钩子
useContext用于接收一个context对象并返回该context的值, 可以实现跨层级的数据共享。 ```javascript import React, { useState, useContext } from ‘react’
const CountContext = React.createContext() export default function App() { const [count, setCount] = useState(10) function updateCount() { setCount(count + 5) } return (
我是APP组件
当前的count为:{count}
function Parent() { return (
我是Parent组件
我时Child组件
父组件传过来的值:{count}
```