V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xubingok
V2EX  ›  React

是否可以只对一个变量使用 useState,用来触发重新渲染即可,其他变量实时计算?

  •  
  •   xubingok · 2023-12-27 10:32:55 +08:00 · 1842 次点击
    这是一个创建于 366 天前的主题,其中的信息可能已经有所发展或是发生改变。

    新手学习 React,最近刚发现如果有变量发生改变,不使用 useState 提供的 setXXX 也是可以的.

    用 React 官方教程中的代码举例:

    import { useState } from 'react';
    
    export default function Counter() {
      const [number, setNumber] = useState(0);
      const [otherNumber, setOtherNumber] = useState(0);
      console.log("重新渲染")
      return (
        <>
          <h1>{number}</h1>
          <h1>{otherNumber}</h1>
          <button onClick={() => {
            setNumber(1);
            setOtherNumber(2);
          }}>+</button>
        </>
      )
    }
    

    效果是点击按钮,修改 number 和 otherNumber 的值,模拟实际使用中请求接口后需要修改多处变量显示的场景.

    据我浅薄的理解,useState 此处应该是修改变量,并通知页面使用修改后的值重新渲染吧?打印的 log 确实也走了两遍.

    所以可不可以这样:只对其中一个变量使用 useState(反正会重走 return 方法),剩下的变量就在 return 前面计算就行了:

    import { useState } from 'react';
    
    export default function Counter() {
      const [number, setNumber] = useState(0);
      let otherNumber  = number == 1 ? 2:1;
      return (
        <>
          <h1>{number}</h1>
          <h2>{otherNumber}</h2>
          <button onClick={() => {
            setNumber(1);
          }}>+</button>
        </>
      )
    }
    

    这里的 let otherNumber = number == 1 ? 2:1;只是很简单的逻辑,实际使用中可能包含复杂的数据处理工作.

    从运行效果来看,似乎也没啥问题......

    这样岂不是可以把最外层的数据 rootData 做个 useState 就行了,里面各个组件从这个 rootData 里面拿.就是不知道对性能有没有影响了.

    目前看的教程是 https://zh-hans.react.dev/learn,对 React 理解还不深,求大佬解惑,谢谢~

    第 1 条附言  ·  2023-12-27 11:33:27 +08:00
    具体说明一下实际场景吧:

    实际使用中一个页面可能有很多 echarts 图表,这些图表数据来自于同一个接口.

    所以我在想,是把接口返回的 data 做一个 useState,然后各个图表直接从里面取呢,还是每个图表都搞一个 useState,接口回来后挨个 setXXX 呢???

    性能方面,大佬们能细说下吗...
    19 条回复    2023-12-27 14:39:54 +08:00
    murmur
        1
    murmur  
       2023-12-27 10:36:52 +08:00
    以前我这么干过,react15 的年代,优化不明白了,我就给组件加个 key ,到时候把 key 改了重渲染,剩下的都不管了,愿意咋样咋样

    意外的效果还不错,那个时候我们还做了 IE 兼容
    AvilCore
        2
    AvilCore  
       2023-12-27 10:45:11 +08:00 via Android
    你这里的 othernumber 不是变量而是常量啊,eslint 会报错
    这样实际上你的网页只有一个变量,商务逻辑需要的话这么做很正常
    lisongeee
        3
    lisongeee  
       2023-12-27 10:47:37 +08:00   ❤️ 1
    > 打印的 log 确实也走了两遍

    你确定不是开发模式下严格模式导致的?

    ---

    你的第一个代码示例的两个 setXXX 处于一个同步任务下,按理说应该会被 react 合并为一个更新
    344457769
        4
    344457769  
       2023-12-27 10:54:15 +08:00
    可以这样写,但是如果你在业务逻辑里直接改变 otherNumber 是不会重新渲染的
    DKburNIng
        5
    DKburNIng  
       2023-12-27 10:55:15 +08:00
    这种简单代码可以,业务复杂了会 render 很多次,你这行三目就要执行很多很多次
    foolnius
        6
    foolnius  
       2023-12-27 11:01:29 +08:00
    你的 otherNumber 定义依赖于 number ,我会考虑用 useMemo
    Hoothin
        7
    Hoothin  
       2023-12-27 11:08:24 +08:00
    不如直接寫成 class + shouldComponentUpdate 了
    paledream
        8
    paledream  
       2023-12-27 11:15:49 +08:00
    `这样岂不是可以把最外层的数据 rootData 做个 useState 就行了,里面各个组件从这个 rootData 里面拿.就是不知道对性能有没有影响了.`
    1. 可以的
    2. 对性能是有影响的
    ZZITE
        9
    ZZITE  
       2023-12-27 11:18:48 +08:00
    应该根据你的需求来决定,这个 otherNumber 需不需要自主更新(主动的去 set 他的值)?如果只是完全依赖于 number 值得变化而变化,那么这样写没什么问题,也不需要 memo 比较,比较也需要开销的。
    panxiuqing
        10
    panxiuqing  
       2023-12-27 11:30:44 +08:00
    `otherNumber` 只是依赖 `number` 的话就不应该定义两个 state 。
    state 是应用外部通知内部状态变化的入口,effect 是应用内部状态变化需要通知外部时的入口。
    只由内部状态计算而来的状态应该用 `useMemo`,不然无关状态导致的重渲染产生了重复计算。
    vincenteof
        11
    vincenteof  
       2023-12-27 11:33:46 +08:00
    这是 react 推荐的,避免多余的 useState ,让数据自己流动
    xubingok
        12
    xubingok  
    OP
       2023-12-27 11:36:53 +08:00
    @lisongeee 我错了..确实是开发模式下导致的~~~~
    xubingok
        13
    xubingok  
    OP
       2023-12-27 11:37:17 +08:00
    @paledream 大佬,细说性能影响~~~
    Yvette
        14
    Yvette  
       2023-12-27 11:38:33 +08:00
    1. React 18 开始已经默认自动 batch 状态更新了,你看到两次渲染的原因是官方文档的代码都会默认开 StrictMode
    2. 绝大多数应用都不需要担心多一次渲染对性能带来的影响,如果有性能问题那肯定是本身代码写得有问题,所以学习初期不用过于担心渲染次数;可以简单粗暴地认为对**实际**性能没有任何影响
    3. 没理解错的话你描述的从最外层传数据的方法确实是很多全局状态管理工具的原理,等你学到了 Context 应该就能清晰很多 https://react.dev/learn/passing-data-deeply-with-context
    4. 你标题里描述的是一个官方推荐的 pattern ,能从现有的 state/props 计算得出的值直接在渲染时计算就好(同样,计算对实际性能的影响完全可以忽略不计,而且如果真的有影响也可以通过 memoization 解决),具体可以看看这个 https://react.dev/learn/choosing-the-state-structure#avoid-redundant-state 以及等你更熟悉一点之后看看这个 https://react.dev/learn/you-might-not-need-an-effect
    xubingok
        15
    xubingok  
    OP
       2023-12-27 11:38:55 +08:00
    @ZZITE
    @panxiuqing
    感谢两位大佬解惑.
    不过关于 useMemo 似乎有不同意见...这个东西我还要细看一下,目前确实只用到 state 和 effect 这两个 hook.
    xubingok
        16
    xubingok  
    OP
       2023-12-27 11:41:12 +08:00
    @Yvette 感谢感谢..
    那我就渲染时直接计算了,搞这么多 useState 还要挨个调用 setXXX 确实挺烦的.
    codehz
        17
    codehz  
       2023-12-27 11:59:03 +08:00 via iPhone
    @xubingok useMemo 是给计算需要一定成本的用的,简单加减乘除甚至字符串连接都不需要。
    我觉得你这个例子里用 setState 没有问题,但是扩展到更大项目里,如果每个子元素的更新都要触发 root 组件的更新的话,就有点问题了,这时候可以考虑引入第三方状态管理库来处理
    冷知识好多第三方状态管理库也都是接入到 react 的 useState (或者更高级的 useSyncExternalStore )来处理,但是针对复杂嵌套状态做了优化,避免了“牵一发动全身”的问题,只会在用到状态的地方触发 setState (或者 sync external store 里的回调)
    xubingok
        18
    xubingok  
    OP
       2023-12-27 14:27:52 +08:00
    @codehz 谢谢.
    xubingok
        19
    xubingok  
    OP
       2023-12-27 14:39:54 +08:00
    @Yvette
    我又看了一遍 https://zh-hans.react.dev/learn/choosing-the-state-structure#avoid-duplication-in-state
    确实有提到这个用法,写得是真好啊~
    大佬实在太实诚了,这个章节正好解答了我的问题~~~
    再次感谢~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2752 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:03 · PVG 21:03 · LAX 05:03 · JFK 08:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.