hooks篇
React内置Hooks
处理状态
useState
: 用于在组件中 添加状态。它返回 一个状态值 和 一个更新状态的函数。useReducer
: 类似于useState
,但是适用于更 复杂的状态逻辑。它接受一个 reducer 函数,用于更新状态。
useState 与 useReducer 的区别
- 使用场景
useState
: 简单的状态管理,例如: 单个值或对象的状态useReducer
: 复杂的状态逻辑,例如: 当状态之间有多个相关操作时,需要将状态和状态更新逻辑分离,使代码更模块化和易于维护
- 使用方法
useState
: 返回一个状态值和一个更新状态的函数useReducer
: 返回当前状态和一个 dispatch 函数,dispatch 函数用于触发状态更新。你需要传递一个 action 对象,根据 action 类型来更新状态。
- 状态更新方式
useState
: 直接替换现有状态useReducer
: 通过使用 reducer 函数来管理状态更新
useState
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
useReducer
import React, { useReducer } from 'react';
function countReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
default:
return state;
}
}
function Counter() {
const [count, dispatch] = useReducer(countReducer, 0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
</div>
);
}
useState实现useReducer的效果
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const dispatch = (action) => {
switch (action.type) {
case 'INCREMENT':
setCount(count + 1);
break;
default:
break;
}
};
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
</div>
);
}
export default Counter;
处理副作用
useEffect
: 用于 处理副作用,如数据获取、订阅、DOM 操作等。在组件 渲染后执行,并且可以在组件 卸载时 执行清理操作。useLayoutEffect
: 类似于useEffect
,但在浏览器 绘制之前同步执行,可用于进行 DOM 操作和测量布局。
useEffect 与 useLayoutEffect 的区别
- 执行时机
useEffect
: 浏览器渲染完成后异步执行,不会阻塞页面的绘制和交互useLayoutEffect
: 浏览器绘制之前同步执行。可能会导致页面阻塞
- 性能影响
useLayoutEffect
: 在大量组件中广泛使用,可能会影响页面性能和用户体验,因为它会阻塞页面绘制
尽可能使用useEffect
,除非是 修改DOM并且不让用户看到修改DOM的过程,才考虑使用useLayoutEffect
性能优化
useCallback
: 用于 优化性能,记忆函数实例,仅在依赖项发生变化时重新创建函数。useMemo
: 用于在组件 渲染期间缓存计算结果,以 避免重复计算,适用于计算昂贵的值。
useCallBack使用场景
- 使用:
- 接受两个参数:要进行优化的函数 和 一个依赖数组,只有数组中的依赖项发生变化时,才会重新创建函数。
- 优化的函数可以接收任何数量的参数,这个参数由返回函数的参数
- 返回值:返回一个记忆化的函数实例,该函数只在依赖项发生变化时才重新创建
- 接受两个参数:要进行优化的函数 和 一个依赖数组,只有数组中的依赖项发生变化时,才会重新创建函数。
- 场景:
- 父组件将一个
函数
作为prop
传递给子组件时。使用useCallback
可以 确保每次渲染时都不会创建新的函数实例,从而减少不必要的重新渲染。
- 父组件将一个
useMemo使用场景
- 使用:
- 接受两个参数:计算函数 和 一个依赖数组,只有数组中的依赖项发生变化时,才会重新计算
- 返回值:返回一个记忆化的值。
- 场景:
- 避免不必要的重复渲染
- 避免接口重复请求
- 返回函数时可以像
useCallBack
一样使用
useCallBack
// Button组件
import React from 'react';
const Button = ({ onClick, label }) => {
console.log('Button rendered:', label);
return <button onClick={onClick}>{label}</button>;
};
export default React.memo(Button);
// 父组件
import React, { useState, useCallback } from 'react';
import Button from './button';
import Container from '../../../_components/container';
const UseCallbackDemo = (props) => {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
// 使用 useCallback 优化回调函数的创建
const handleIncrement1 = useCallback(() => {
setCount1(count1 + 1);
}, [count1]);
const handleIncrement2 = useCallback(() => {
setCount2(count2 + 1);
}, [count2]);
// const handleIncrement1 = () => {
// setCount1(count1 + 1);
// };
// const handleIncrement2 = () => {
// setCount2(count2 + 1);
// };
return (
<Container {...props} className="usecallback-container">
<Button onClick={handleIncrement1} label={`Count 1: ${count1}`} />
<Button onClick={handleIncrement2} label={`Count 2: ${count2}`} />
</Container>
);
};
export default UseCallbackDemo;
useMemo
import React, { useState, useMemo } from 'react';
const NumberList = ({ numbers }) => {
return (
<ul>
{numbers.map((number) => (
<li key={number}>Square of {number}: {number * number}</li>
))}
</ul>
);
};
const UseMemoDemo = (props) => {
const [numbers, setNumbers] = useState([1, 2, 3, 4, 5]);
const [count, setCount] = useState(0);
// 使用 useMemo 优化计算平方
const squaredNumbers = useMemo(() => {
console.log('🍉 squaredNumbers 重新计算 ');
return numbers.map((number) => number * number);
}, [numbers]);
return (
<Container {...props} className="usecallback-container">
<h1>Number Squares</h1>
<NumberList numbers={squaredNumbers} />
<Button type="primary" size="sm" onClick={() => setCount(count + 1)}>count: {count}</Button>
<br />
<br />
<Button type="primary" size="sm" onClick={() => setNumbers(numbers.map(n => n + 1))}>numbers: {numbers}</Button>
</Container >
);
};
export default UseMemoDemo;
组件上下文
useContext
: 用于访问 React 上下文。允许你在组件中订阅上下文的变化。
MyContext
import React, { createContext } from 'react';
// 创建一个上下文
const MyContext = createContext();
export default MyContext;
父组件
import React from 'react';
import MyContext from './MyContext';
const ParentComponent = () => {
const sharedData = "This is shared data";
return (
<MyContext.Provider value={sharedData}>
{/* 在这里的子组件可以访问到共享的数据 */}
<ChildComponent />
</MyContext.Provider>
);
};
export default ParentComponent;
子组件
import React, { useContext } from 'react';
import MyContext from './MyContext';
const ChildComponent = () => {
// 使用 useContext 来获取上下文数据
const sharedData = useContext(MyContext);
return <div>{sharedData}</div>;
};
export default ChildComponent;
保存值/性能优化
useRef
: 返回一个可变的 ref 对象,可以用于 保存组件内的值,而 不会导致重新渲染。
暴露实例属性与方法
useImperativeHandle
: 用于自定义组件暴露给父组件的实例值,通常与forwardRef
一起使用。
子组件
import React, { forwardRef, useImperativeHandle, useState } from 'react';
const ChildComponent = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
// 在子组件内定义一个暴露给父组件的方法
const increment = () => {
setCount(count + 1);
};
// 使用 useImperativeHandle 暴露方法和属性给父组件
useImperativeHandle(ref, () => ({
increment, // 暴露的方法
count // 暴露的属性
}));
return <div>Count: {count}</div>;
});
export default ChildComponent;
父组件
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const childRef = useRef();
const handleButtonClick = () => {
// 调用子组件的方法
childRef.current.increment();
};
return (
<div>
<ChildComponent ref={childRef} />
<button onClick={handleButtonClick}>Increment Child Count</button>
</div>
);
}
export default ParentComponent;
React-Router内置Hooks
useHistory
import { useHistory } from 'react-router-dom';
function MyComponent() {
const history = useHistory();
const handleClick = () => {
// 在点击时导航到另一个路由
history.push('/another-route');
};
return (
<div>
<button onClick={handleClick}>Go to Another Route</button>
</div>
);
}
useLocation
import { useLocation } from 'react-router-dom';
function MyComponent() {
const location = useLocation();
return (
<div>
<p>Current Path: {location.pathname}</p>
</div>
);
}
useParams
import { useParams } from 'react-router-dom';
function UserProfile() {
const { username } = useParams();
return (
<div>
<p>Username: {username}</p>
</div>
);
}
useRouteMatch 是否路由匹配
import { useRouteMatch } from 'react-router-dom';
function MyComponent() {
const match = useRouteMatch('/some-route');
return (
<div>
{match ? <p>Matched!</p> : <p>Not Matched</p>}
</div>
);
}