详解React如何优雅地根据prop更新state值

 更新时间:2023年11月14日 08:22:58   作者:晓得迷路了  
这篇文章主要为大家详细介绍了React如何优雅地实现根据prop更新state值,文中的示例代码讲解详细,具有一定的参考价值,感兴趣的小伙伴可以了解下

快速总结:

场景

开发 React 组件中,有时会碰到同步状态的问题,就是当 prop 变化时调整组件的 state 值。

比如以下这个场景:一个下拉组件 Select 内置了两组选项内容,支持通过 type 来选择,同时也支持传入 options 参数自定义选项内容。

代码实现方面,内部维护一个 innerOptions,外部参数 typeoptions 通过 useEffect 来同步,相关代码如下:

import React, { useState, useEffect } from "react";
import { Flex, Space, Select } from "antd";

function MySelect({ value, onChange, type, options }) {
  const optionsList = [[
    { value: "dog", label: "小狗" },
    { value: "cat", label: "小猫" },
  ], [
    { value: "banana", label: "香蕉" },
    { value: "apple", label: "苹果" },
  ]]
  const [innerOptions, setInnerOptions] = useState(options || optionsList[type] || [])

  useEffect(() => {
    if (type !== undefined) {
      setInnerOptions(optionsList[type])
    }
  }, [type]);

  useEffect(() => {
    if (options !== undefined) {
      setInnerOptions(options)
    }
  }, [options])

  return (
    <Space direction="vertical">
      {`你选择了:${innerOptions.find((item) => item.value === value)?.label}`}
      <Select
        style={{ width: 120 }}
        value={value}
        onChange={onChange}
        options={innerOptions}
      />
    </Space>
  );
}

正常使用这个组件时一切都是正常的。

问题出现

不过当你需要在一个 Select 组件中切换 type 时,就会出现一个问题,切换过程中间会闪现过一个 undefined

import React, { useState } from "react;
import { Flex, Space } from "antd";
import MySelect from "./MySelect";

function App() {
  const [value, setValue] = useState("dog");
  const [type, setType] = useState(0);

  const handleChange = (newVal) => {
    setValue(newVal);
  };

  const handleChangeType = (e) => {
    setType(e.target.value);
    setValue(e.target.value === 0 ? 'dog' : 'banana')
  }

  return (
    <div className="App">
      <Flex gap="middle" vertical>
        <Radio.Group onChange={handleChangeType} value={type}>
          <Radio value={0}>动物</Radio>
          <Radio value={1}>水果</Radio>
        </Radio.Group>
        <MySelect type={type} value={value} onChange={handleChange} />
      </Flex>
    </div>
  );
}

问题的原因是切换选项时,value 值是直接更新的,而 innerOptionsuseEffect 中更新的,也就是中间会出现一次 value 为新值,而 innerOptions 为旧值的渲染。

寻找解决方案

既然找到了原因,就开始想办法处理这个问题。

useLayoutEffect

第一时间想到了用 useLayoutEffect 能否解决, useLayoutEffect 在 react 文档中说明是在将内容真正渲染到屏幕前调用,并且会阻塞浏览器重绘。

将 useEffect 全部改成 useLayoutEffect。

useLayoutEffect(() => {
    if (type !== undefined) {
      setInnerOptions(optionsList[type])
    }
}, [type]);

useLayoutEffect(() => {
    if (options !== undefined) {
      setInnerOptions(options)
    }
}, [options])

可以看到不会出现 undefined 的情况了。

useLayoutEffect 会降低性能。在可能的情况下,最好使用 useEffect。

不过官方文档有提到 useLayoutEffect 会降低性能,再看看是否有更好的方案。

react 最佳实践

prop 改变后 state 同步这个在 react 文档中有编码建议。

官方推荐不要使用 useEffect,在函数中直接调整 state 值。

const [innerOptions, setInnerOptions] = useState(options || optionsList[type] || [])
const [prevType, setPrevType] = useState(type);
const [prevOptions, setPrevOptions] = useState(options);

if (prevType !== type) {
    setPrevType(type);
    setInnerOptions(optionsList[type])
}

if (prevOptions !== options) {
    setPrevOptions(type);
    setInnerOptions(options)
}

上面的代码比起使用 useEffect 的代码可能并不常见。

当你在组件渲染函数中直接更新组件时,React 会丢弃返回的 JSX 并立即重新渲染。不过为了避免非常缓慢的级联重试,React 只允许你在组件函数中更新同一组件的状态。

也就是说 value 为新值,而 innerOptions 为旧值的渲染被丢弃了,所以不会出现 undefined 的情况,问题也得到了解决。

总结

正常情况下,我们使用 useEffect 来将 prop 更新到 state 是没问题的。不过在有界面渲染的情况下,可能会有 bug 出现,这时需要使用 useLayoutEffect 或者直接在组件渲染函数中更新 state 值。

其实根据 prop 更新 state 在非必要的情况下尽量不要出现,优先考虑在渲染函数中直接根据 prop 计算状态或者通过 key 值重新渲染整个组件

例如以下方式处理 innerOptions:

// 直接根据 prop 计算状态
const innerOptions = optionsList[type] || options || []

以上就是详解React如何优雅地根据prop更新state值的详细内容,更多关于React更新state值的资料请关注脚本之家其它相关文章!

相关文章

  • react-router JS 控制路由跳转实例

    react-router JS 控制路由跳转实例

    这篇文章主要介绍了react-router JS 控制路由跳转实例,react实现路由可以直接使用react-router。有兴趣的可以了解一下
    2017-06-06
  • 详解React中的todo-list

    详解React中的todo-list

    这篇文章主要介绍了React中的todo-list的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-07-07
  • React中mobx和redux的区别及说明

    React中mobx和redux的区别及说明

    这篇文章主要介绍了React中mobx和redux的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • React中使用TS完成父组件调用子组件的操作方法

    React中使用TS完成父组件调用子组件的操作方法

    由于在项目开发过程中,我们往往时需要调用子组件中的方法,这篇文章主要介绍了React中使用TS完成父组件调用子组件,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • react中将html字符串渲染到页面的实现方式

    react中将html字符串渲染到页面的实现方式

    这篇文章主要介绍了react中将html字符串渲染到页面的实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • react-router 重新加回跳转拦截功能详解

    react-router 重新加回跳转拦截功能详解

    这篇文章主要为大家介绍了react-router 重新加回跳转拦截功能详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • ReactiveCocoa代码实践之-UI组件的RAC信号操作

    ReactiveCocoa代码实践之-UI组件的RAC信号操作

    这篇文章主要介绍了ReactiveCocoa代码实践之-UI组件的RAC信号操作 的相关资料,需要的朋友可以参考下
    2016-04-04
  • react native环境安装流程

    react native环境安装流程

    React Native 是目前流行的跨平台移动应用开发框架之一。本文介绍react native环境安装流程及遇到问题解决方法,感兴趣的朋友一起看看吧
    2021-05-05
  • React 跨端动态化核心技术实例分析

    React 跨端动态化核心技术实例分析

    这篇文章主要为大家介绍了React 跨端动态化核心技术实例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • React生命周期方法之componentDidMount的使用

    React生命周期方法之componentDidMount的使用

    这篇文章主要介绍了React生命周期方法之componentDidMount的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06

最新评论