React

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > React > React更新state值

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

作者:晓得迷路了

这篇文章主要为大家详细介绍了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值的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文