函数式组件
useState
const [state, setState] = useState(initialState);
initialState
- 可以是一个默认值作为 state 的初始值;
- 也可以是一个无参函数,我们可以在里面进行复杂的运算,然后返回值作为 state 初始值,该函数只有组件首次渲染时才会被调用。
const [count, setCount] = useState(0);
const [count, setCount] = useState(() => {
return props.intialValue;
});
setState
- 可以传入
nextState
来更新 state; - 也可以传入一个函数,该函数可以接收到
preState
参数,其返回值可以更新 state; - 忽略更新的情况:「传入的
nextState
」和「传入函数的返回值」会与当前 state 进行浅比较 (Object.is),如果相同则会忽略本次更新。
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
}
const [count, setCount] = useState(0);
const handleClick = () => {
setCount((preState) => {
return preState + 1;
});
}
const [count, setCount] = useState(0);
// 函数组件只会在挂载时执行一次,后续再点击按钮则不会再执行
const handleClick = () => {
setCount(count + 0);
}
important
函数组件中的 setState
同样具有类组件中的 this.setState
的批处理机制
useEffect
useEffect(() => {
/* 进行副作用操作 */
return () => {
/* 进行副作用清除 */
};
}, deps);
在函数组件的函数体内部(函数体执行过程中),改变 DOM、添加订阅和监听、进行异步操作、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性。
但是在函数组件中,我们可以使用 useEffect
来处理这些包含副作用的操作,使用它时第一个参数需要传入一个函数,第二个参数传入一个依赖数组。
deps 缺省
useEffect(() => {
return () => {}
});
deps 缺省时,每次函数组件执行完后,并且内容已经渲染到页面之后,就会执行 useEffect
中的回调,回调中的清除函数会在第二次执行完函数组件后,并且内容已经渲染到页面中,在 useEffect
的回调执行之前执行。
import React, { Component } from "react";
const Counter = (props) => {
const [count, setCount] = React.useState(0);
const handleClick = () => {
setCount(count + 1);
}
React.useEffect(() => {
document.getElementById('btn').innerHTML = 10;
return () => {
document.getElementById('btn').innerHTML = 9;
}
});
return <button id="btn" onClick={handleClick}>{count}</button>;
}
首次执行完函数组件,button 内容会从 0 变为 10。
点击按钮后,button 内容会从 1 变为 9 再变为 10。
important
useEffect
中传入第一个参数的函数会被 延迟调用,这一点和 componentDidMount
、componentDidUpdate
有所不同,因为类组件在挂载完毕或者更新完毕后,这两个生命周期函数是会被立刻调用的,并没有 useEffect 的 "延迟感"。如果想要消除 useEffect 的 "延迟感",那么可以使用 useLayoutEffect
。
deps 为空数组
useEffect(() => {
return () => {}
}, []);
只有在函数组件首次执行完毕后,并且组件已经渲染到页面中,才会执行回调,之后函数组件无论执行多少次都不会再执行 useEffect
中的回调,类比于 componentDidMount
。
内部的清除函数只有当组件即将卸载时才会执行,类比于 componentWillUnmount
。
deps 存在依赖元素
const [count, setCount] = useState(0);
useEffect(() => {
return () => {}
}, [count]);
函数组件在首次渲染完毕后,会执行 useEffect
中的回调。之后组件重新渲染后,只有当依赖数组中的元素取值发生变化时才会执行回调。
内部的清除函数在组件首次挂载渲染 ( 执行 ) 后并不会执行,只有当 count 发生改变后才会进行调用,并且内部引用的 props 和 state 均为上一次渲染的,执行顺序是:
setState(count + 1)
,函数组件重新渲染 (调用)- 函数组件重新渲染 (调用) 完毕后,调用清除函数,用于清除上一次 effect
- 执行 useEffect 回调
important
- 虽然
useEffect
会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。React 将在组件更新前刷新上一轮渲染的 effect。 useLayoutEffect
和useEffect
的结构相同,区别只是前者消除了 回调函数调用时的 "延迟感"。- 关于
useEffect
的依赖数组,官方建议请确保数组中包含了所有外部作用域中会发生变化且在 effect 中使用的变量,否则你的代码会引用到先前渲染中的旧变量