跳到主要内容

hooks篇

React内置Hooks

处理状态

  1. useState: 用于在组件中 添加状态。它返回 一个状态值一个更新状态的函数
  2. useReducer: 类似于 useState,但是适用于更 复杂的状态逻辑。它接受一个 reducer 函数,用于更新状态。
useState 与 useReducer 的区别
  1. 使用场景
    • useState: 简单的状态管理,例如: 单个值或对象的状态
    • useReducer: 复杂的状态逻辑,例如: 当状态之间有多个相关操作时,需要将状态和状态更新逻辑分离,使代码更模块化和易于维护
  2. 使用方法
    • useState: 返回一个状态值和一个更新状态的函数
    • useReducer: 返回当前状态和一个 dispatch 函数,dispatch 函数用于触发状态更新。你需要传递一个 action 对象,根据 action 类型来更新状态。
  3. 状态更新方式
    • 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;

处理副作用

  1. useEffect: 用于 处理副作用,如数据获取、订阅、DOM 操作等。在组件 渲染后执行,并且可以在组件 卸载时 执行清理操作。
  2. useLayoutEffect: 类似于 useEffect,但在浏览器 绘制之前同步执行,可用于进行 DOM 操作和测量布局。
useEffect 与 useLayoutEffect 的区别
  1. 执行时机
    • useEffect: 浏览器渲染完成后异步执行,不会阻塞页面的绘制和交互
    • useLayoutEffect: 浏览器绘制之前同步执行。可能会导致页面阻塞
  2. 性能影响
    • useLayoutEffect: 在大量组件中广泛使用,可能会影响页面性能和用户体验,因为它会阻塞页面绘制

尽可能使用useEffect,除非是 修改DOM并且不让用户看到修改DOM的过程,才考虑使用useLayoutEffect

Demo

性能优化

  1. useCallback: 用于 优化性能记忆函数实例,仅在依赖项发生变化时重新创建函数。
  2. useMemo: 用于在组件 渲染期间缓存计算结果,以 避免重复计算,适用于计算昂贵的值。
useCallBack使用场景
  1. 使用:
    • 接受两个参数:要进行优化的函数 和 一个依赖数组,只有数组中的依赖项发生变化时,才会重新创建函数。
      • 优化的函数可以接收任何数量的参数,这个参数由返回函数的参数
    • 返回值:返回一个记忆化的函数实例,该函数只在依赖项发生变化时才重新创建
  2. 场景:
    • 父组件将一个函数作为 prop 传递给子组件时。使用 useCallback 可以 确保每次渲染时都不会创建新的函数实例,从而减少不必要的重新渲染

Demo

useMemo使用场景
  1. 使用:
    • 接受两个参数:计算函数 和 一个依赖数组,只有数组中的依赖项发生变化时,才会重新计算
    • 返回值:返回一个记忆化的值。
  2. 场景:
    • 避免不必要的重复渲染
    • 避免接口重复请求
    • 返回函数时可以像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;

组件上下文

  1. 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;

保存值/性能优化

  1. useRef: 返回一个可变的 ref 对象,可以用于 保存组件内的值,而 不会导致重新渲染

暴露实例属性与方法

  1. 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>
);
}

自定义Hooks - ahooks

ahooks列表