【react常见hooks】
除了 React 官方内置的常用 Hooks(像 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef 等 ),开发者还能自己写 自定义 Hooks(把重复逻辑封装成 Hook ,方便复用 )。
useState
import React, { useState } from 'react';
function Counter() {
// count 是当前状态,setCount 是更新它的函数,初始值设为 0
const [count, setCount] = useState(0);
return (
<div>
<p>当前计数: {count}</p>
{/* 点击按钮,调用 setCount 让 count +1 ,触发组件重新渲染 */}
<button onClick={() => setCount(count + 1)}>增加计数</button>
</div>
);
}
useContext
import { createContext, useContext } from "react";
export default function App5() {
return (
<div>
<Section>
<Heading>标题1</Heading>
<Section>
<Heading>标题2</Heading>
<Section>
<Heading>标题3</Heading>
<Section>
<Heading>标题4</Heading>
<Section>
<Heading>标题5</Heading>
<Section>
<Heading>标题6</Heading>
</Section>
</Section>
</Section>
</Section>
</Section>
</Section>
</div>
);
}
const levelContext = createContext(0);
const Heading = ({ children }) => {
const level = useContext(levelContext);
switch (level) {
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw new Error(`Unsupported level: ` + level);
}
};
// 每次调用会走一遍而不是最终值,这是关键,遇到一次执行一次
const Section = ({ children }) => {
const level = useContext(levelContext);
return (
<section className="section">
<levelContext.Provider value={level + 1}>
{children}
</levelContext.Provider>
</section>
);
};
useEffect
import { useEffect, useState } from "react";
// useEffect用法
/**
* 说明
useEffect 的本质:在组件渲染后执行副作用代码,并在依赖变化或组件卸载时清理副作用。
依赖数组 [count]:只有 count 变化时才会重新执行 effect。
返回的函数就是“清理函数”,在 effect 重新执行前或组件卸载时调用。
本质:与组件渲染相关,与组件生命周期相关,额外执行的代码
*/
export default function App7() {
const [count, setCount] = useState(0);
// useEffect 的本质:每次渲染后执行副作用
useEffect(() => {
console.log("组件渲染或 count 变化时执行,当前count:", count);
// 返回一个清理函数,组件卸载或依赖变化前执行
return () => {
console.log("清理副作用,count:", count);
};
}, [count]); // 依赖 count,count 变化时重新执行
return (
<div>
<p>count: {count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize); // 清理
};
}, []);
/**
useEffect(() => { ... }, []) 的含义
[] 表示依赖项为空,即这个 useEffect 只会在组件首次挂载(渲染)时执行一次,之后组件更新时不会再执行。
只有在组件卸载时,才会执行 return 的清理函数。
*/
如果没有 return 这个清理函数,事件监听会一直存在,组件卸载后还会响应事件,造成内存泄漏和异常。
总结:
return 的清理函数是用来“撤销”上一次副作用,防止副作用残留,保证组件行为正确、资源不泄漏。
如果你有具体的副作用代码,我可以帮你分析是否需要清理函数!
类似于java spring里面的后置处理器,可以夹带私货
useReducer
// useReducer
import { useReducer } from "react";
const countReducer = (state, action) => {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
throw new Error("Invalid action");
}
};
// useReducer 看成是协议/绑定的关键词, [count, dispatch] 和 countReducer 绑定,分别又对应state, action
export default function App6() {
const [count, dispatch] = useReducer(countReducer, 0);
return (
<div>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<span
style={{
margin: "0 10px",
// 固定宽度
width: "100px",
textAlign: "center",
}}
>
{count}
</span>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</div>
);
}
useCallback
// useCallback
/**
* 场景说明
假设你有一个父组件,里面有一个回调函数传递给子组件。
每次父组件渲染时,如果不用 useCallback,
回调函数都会重新创建,导致子组件不必要的重新渲染。
*
*/
import React, { useState, useCallback } from "react";
// 子组件,只有 props.onClick 变化才会重新渲染
const Child = React.memo(({ onClick }) => {
console.log("Child 渲染");
return <button onClick={onClick}>点我</button>;
});
function Parent() {
const [count, setCount] = useState(0);
console.log("Parent 渲染");
// 没有 useCallback,每次 Parent 渲染都会生成新函数
// const handleClick = () => {
// console.log('点击了');
// };
// 用 useCallback,只有 count 变化时才会生成新函数
const handleClick = useCallback(() => {
console.log("点击了");
}, []); // 依赖项为空,handleClick 永远不会变
return (
<div>
<p>count: {count}</p>
<button onClick={() => setCount(count + 1)}>加一</button>
<Child onClick={handleClick} />
</div>
);
}
export default Parent;
/**
* React.memo 是 React 提供的一个高阶组件(Higher Order Component,简称 HOC),
* 用于优化函数组件的性能。
* 作用
它的作用是:记忆(缓存)组件的渲染结果,只有当 props 发生变化时,才会重新渲染组件。
如果 props 没变,React 会跳过渲染,直接复用上一次的结果。
*/
点击 加一,父组件本质会执行函数(组件就是函数),此时 Child 理论上函数也会执行;但是由于用了 React.memo,Child 只要 Props属性不变,Child是可以只加载一次,但上面的函数不用useCallback,发现Child 会跟随加载,==》 表明 Props 发生改变,就表明父组件的 handleClick 有更新加载到内存(并未触发);
如果父组件传给子组件的函数使用了useCallback,那么子组件只会走一次,后续只有父组件在走,
// 看一个例子
const handleClick = (() => {
console.log("点击了");
})();
# 如果使用立即执行函数
注意:
这里的 handleClick 实际上不是一个函数,而是立即执行函数表达式(IIFE)的返回值。
也就是说,这段代码会在组件渲染时立刻执行一次 console.log("点击了"),并且 handleClick 的值是 undefined(因为 IIFE 没有返回值)。
React.memo 会对 props 做浅比较,发现 onClick 没变(一直是 undefined),所以 Child 不会重新渲染
useMemo
import { useState, useMemo } from "react";
// useMemo
export default function App13() {
const [x, setX] = useState(0);
const [y, setY] = useState(0);
console.time("xxx");
const a = useMemo(() => {
let a = 0;
for (let i = 0; i < 1000000000; i++) {
a += y;
}
return a;
}, [y]);
console.timeEnd("xxx");
return (
<div>
<div>x : {x}</div>
<div>y : {y}</div>
<div>a : {a}</div>
<button onClick={() => setX(x + 1)}>x + 1</button>
<button onClick={() => setY(y + 1)}>y + 1</button>
</div>
);
}
useRef
export default function App14() {
let timer;
const handleClick = () => {
clearInterval(timer);
timer = setInterval(() => {}, 1000);
};
// useRef
return (
<>
<button onClick={handleClick}>开始</button>
<div>hello world</div>
</>
);
}
/**
* 存在一个问题,组件每次渲染,重新执行handleClick,导致定时器重新开始
*
* export default function App14() {
let timer = useRef(null);
const handleClick = () => {
clearInterval(timer.current);
timer.current = setInterval(() => {}, 1000);
};
// useRef
return (
<>
<button onClick={handleClick}>开始</button>
<div>hello world</div>
</>
);
}
*/



浙公网安备 33010602011771号