React Hooks 常见问题及解决方案

React Hooks 常见问题及解决方案

常见问题

  • 🐤 useState 和 setState 有什么明显的区别?

  • 🐤 useState 和 useReducer 的初始值如果是个执行函数返回值,执行函数是否会多次执行?

  • 🐤 还原 useReducer 的初始值,为什么还原不回去了?

  • 🐤 useEffect 如何模拟 componentDidMount、componentUpdate、componentWillUnmount 生命周期?

  • 🐤 如何在 useEffect 中正确的为 DOM 设置事件监听?

  • 🐤 useEffect、useCallback、useMemo 中取到的 state、props 中为什么会是旧值?

  • 🐤 useEffect 为什么会出现无限执行的问题?

  • 🐤 useEffect 中出现竞态如何解决?

  • 🐤 如何在函数组件中保存一些属性,跟随组件进行创建和销毁?

  • 🐤 当 useCallback 会频繁触发时,应该如何进行优化?

  • 🐤 useCallback 和 useMemo 的使用场景有何区别?

一、函数组件渲染过程

先来看一下函数组件的运作方式:

Counter.js

function Counter() {
   
    const [count, setCount] = useState(0);

    return <p onClick={
   () => setCount(count + 1)}>count: {
   count}</p>;
}

每次点击 p 标签,count 都会 + 1,setCount 会触发函数组件的渲染。函数组件的重新渲染其实是当前函数的重新执行。
在函数组件的每一次渲染中,内部的 state、函数以及传入的 props 都是独立的。

比如:

// 第一次渲染
function Counter() {
   
    // 第一次渲染,count = 0
    const [count, setCount] = useState(0);

    return <p onClick={
   () => setCount(count + 1)}>clicked {
   count} times</p>;
}


// 点击 p 标签触发第二次渲染
function Counter() {
   
    // 第二次渲染,count = 1
    const [count, setCount] = useState(0);

    return <p onClick={
   () => setCount(count + 1)}>clicked {
   count} times</p>;
}

// 点击 p 标签触发第三次渲染
function Counter() {
   
    // 第三次渲染,count = 2
    const [count, setCount] = useState(0);

    return <p onClick={
   () => setCount(count + 1)}>clicked {
   count} times</p>;
}

// ...

在函数组件中声明的方法也是类似。因此,在函数组件渲染的每一帧对应这自己独立的 statefunctionprops

二、useState / useReducer

useState VS setState

  • useState 只能作用在函数组件,setState 只能作用在类组件

  • useState 可以在函数组件中声明多个,而类组件中的状态值都必须声明在 thisstate 对象中

  • 一般的情况下,state 改变时:

    • useState 修改 state 时,同一个 useState 声明的值会被 覆盖处理,多个 useState 声明的值会触发 多次渲染

    • setState 修改 state 时,多次 setState 的对象会被 合并处理

  • useState 修改 state 时,设置相同的值,函数组件不会重新渲染,而继承 Component 的类组件,即便 setState 相同的值,也会触发渲染

useState VS useReducer

初始值
  • useState 设置初始值时,如果初始值是个值,可以直接设置,如果是个函数返回值,建议使用回调函数的方式设置
const initCount = c => {
   
    console.log('initCount 执行');
    return c * 2;
};

function Counter() {
   
    const [count, setCount] = useState(initCount(0));

    return <p onClick={
   () => setCount(count + 1)}>clicked {
   count} times</p>;
}

会发现即便 Counter 组件重新渲染时没有再给 count 重新赋初始值,但是 initCount 函数却会重复执行

修改成回调函数的方式:

const initCount = c => {
   
    console.log('initCount 执行');
    return c * 2;
};

function Counter() {
   
    const [count, setCount] = useState(() => initCount(0));

    return <p onClick={
   () => setCount(count + 1)}>clicked {
   count} times</p>;
}

这个时候,initCount 函数只会在 Counter 组件初始化的时候执行,之后无论组件如何渲染,initCount 函数都不会再执行

  • useReducer 设置初始值时,初始值只能是个值,不能使用回调函数的方式
    • 如果是个执行函数返回值,那么在组件重新渲染时,这个执行函数依然会执行
修改状态
  • useState 修改状态时,同一个 useState 声明的状态会被覆盖处理
function Counter() {
   
    const [count, setCount] = useState(0);

    return (
        <p
            onClick={
   () => {
   
                setCount(count + 1);
                setCount(count + 2);
            }}
        >
            clicked {
   count} times
        </p>
    );
}

当前界面中 countstep 是 2

在这里插入图片描述

  • useReducer 修改状态时,多次 dispatch 会按顺序执行,依次对组件进行渲染
function Counter() {
   
    const [count, dispatch] = useReducer((x, payload) => x + payload, 0);

    return (
        <p
            onClick={
   () => {
   
                dispatch(1);
                dispatch(2);
            }}
        >
            clicked {
   count} times
        </p>
    );
}

当前界面中 countstep 是 3

在这里插入图片描述

还原 useReducer 的初始值,为什么还原不了

比如下面这个例子:

const initPerson = {
    name: '小明' };

const reducer = function (state, action) {
   
    switch (action.type) {
   
        case 'CHANGE':
            state.name = action.payload;
            return {
    ...state };
        case 'RESET':
            return initPerson;
        default:
            return state;
    }
};

function Counter() {
   
    const [person, dispatch] = useReducer(reducer, initPerson);
    const [value, setValue
  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
React Hooks是React 16.8版本中新增的特性,它提供了一种在函数组件中使用状态和其他React特性的方式。使用Hooks可以使函数组件具备类组件的功能,而不需要使用类组件的繁琐语法和复杂的生命周期方法。在实际项目中使用React Hooks可以带来更好的开发体验和代码复用。 在使用React Hooks进行实战时,可以通过useState和useEffect等Hook来处理组件中的状态和副作用。useState可以用于在函数组件中创建和管理状态,而useEffect可以用于处理副作用操作,例如发送网络请求和订阅事件等。同时,还可以使用其他常用的Hooks,比如useRef和useCallback等。 在团队协作方面,引入React Hooks可能会导致团队成员对于Hooks的水平不一致。一些常见问题包括闭包问题和依赖死循环等。解决闭包问题可以使用不同的方法,React官方并没有给出太多的最佳实践,所以团队成员需要通过实践摸索出适合自己的方式。此外,在刚接触Hooks时,团队成员可能只熟悉useState和useEffect这两个最常用的Hooks,对于其他常用的Hooks的使用场景可能不太清楚。 为了避免一些常见问题,可以定期在团队内分享React Hooks的最佳实践,引导团队成员更好地理解和使用Hooks。对于那些不喜欢使用React Hooks的成员,可以继续使用类组件,因为类组件在某些方面可能更容易理解和调试。React Hooks只是一种工具,会使用它会提升开发效率,但不使用也不会影响其他人的代码。因此,类组件和函数组件可以共存。 总之,React Hooks是一个强大的工具,可以在函数组件中使用状态和其他React特性。在实际项目中,使用React Hooks可以带来更好的开发体验和代码复用,但需要注意团队成员之间的水平差异和常见问题的解决方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [React hooks实战总结](https://blog.csdn.net/sinat_17775997/article/details/100014480)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [React-Hook最佳实践](https://blog.csdn.net/xiaofeng123aazz/article/details/127361597)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值