函数组件模拟生命周期
创建方式
jsx
const Hello = (props)=>{
return <div>{props.message}</div>
}
const Hello = props => <div>{props.message}</div>
function Hello(props){
return <div>{props.message}</div>
}
函数组件没有 state
,用的是 useState
函数组件没有生命周期,用到是 useEffect
(React v16.8.0推出Hooks API)。
TIP
useEffect
的作用是解决问题。
UseEffect
componentDidMount
模拟componentDidMount
,设置为 []
(即不使用任何响应式值) 表示只在第一次渲染的时候调用。
jsx
useEffect(()=>{console.log('use effect')},[])
componentDidUpdate
模拟componentDidUpdate
,[参数]
接受的参数(可以多个)表示依赖的响应式值,当依赖的值发生变化时会进行同步。
jsx
useEffect(()=>{console.log(' n和m change')})
useEffect(()=>{console.log(' n change')},[n])
componentWillUnmount
模拟componentWillUnmount
,在 return
返回的函数,可以处理副作用。
jsx
useEffect(()=>{
console.log('child渲染或变化了')
return ()=>{ console.log('child消失了')}
})
constructor
函数组件执行的时候,就相当于 constructor
。
shouldComponentUpdate
React.memo
和 useMemo
可以解决。
render
函数组件的返回值就是 render
的返回值。
useEffect 区别
没有依赖数组和使用空数组 []
作为依赖组,行为是不同的。
jsx
useEffect(() => {
// 这里的代码会在每次渲染后运行
});
useEffect(() => {
// 这里的代码只会在组件挂载(首次出现)时运行
}, []);
useEffect(() => {
// 这里的代码不但会在组件挂载时运行,而且当 a 或 b 的值自上次渲染后发生变化后也会运行
}, [a, b]);
Effect 会执行两次的问题
在React开发环境 useEffect
默认会执行两次,这是 React 18 引入的 严格模式(Strict Mode) 的故意设计行为,目的是帮助开发者提前发现潜在问题。
那么如何解决这个问题呢?
- 实现清理函数(推荐)
jsx
useEffect(() => {
const timer = setInterval(() => {
console.log('Timer running');
}, 1000);
// 清理函数:必须实现!
return () => clearInterval(timer);
}, []); // 依赖数组为空,仅在挂载/卸载时运行
关键点
始终编写清理函数
(return () => {...})
,避免内存泄漏。确保 Effect 逻辑是幂等的(多次执行结果一致)。
合理设置依赖数组(
[]
、[state]
),避免不必要的重复执行。
- 禁用严格模式(不推荐)
jsx
// 不推荐!仅用于临时调试
ReactDOM.createRoot(document.getElementById('root')).render(
// <React.StrictMode>
<App />
// </React.StrictMode>
);
警告
失去 React 对潜在问题的检测能力,可能导致生产环境出现未清理的副作用。
- 使用
Ref
控制执行次数
通过 useRef
标记是否已执行,避免重复操作(如数据请求):
jsx
const hasFetched = useRef(false);
useEffect(() => {
if (!hasFetched.current) {
fetchData();
hasFetched.current = true;
}
}, []);
自定义Hook之useUpdate
模拟 componentDidUpdate
时,第一次渲染也会执行,消除第一次渲染需要自定一个 useUpdate
。
jsx
import {useEffect, useState} from "react"
const useUpdate = (fn, dep) => {
const [count, setCount] = useState(0)
useEffect(() => {
setCount(x => x + 1)
}, [dep])
useEffect(() => {
if (count > 1) {
fn()
setCount(x => x - 1)
}
}, [count, fn])
}
export default useUpdate
App.js
内调用自定义 useUpdate
。
jsx
import {useState} from "react"
import useUpdate from "./useUpdate"
function App() {
const [n, setN] = useState(0)
const onclick = () => {setN(n + 1)}
useUpdate(() => {console.log('number change')}, n);
return (
<div>{n}<button onClick={onclick}>+1</button></div>
)
}
export default App;