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

2023-12-27 10:32:55 +08:00
 xubingok

新手学习 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 理解还不深,求大佬解惑,谢谢~

1878 次点击
所在节点    React
19 条回复
murmur
2023-12-27 10:36:52 +08:00
以前我这么干过,react15 的年代,优化不明白了,我就给组件加个 key ,到时候把 key 改了重渲染,剩下的都不管了,愿意咋样咋样

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

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

---

你的第一个代码示例的两个 setXXX 处于一个同步任务下,按理说应该会被 react 合并为一个更新
344457769
2023-12-27 10:54:15 +08:00
可以这样写,但是如果你在业务逻辑里直接改变 otherNumber 是不会重新渲染的
DKburNIng
2023-12-27 10:55:15 +08:00
这种简单代码可以,业务复杂了会 render 很多次,你这行三目就要执行很多很多次
foolnius
2023-12-27 11:01:29 +08:00
你的 otherNumber 定义依赖于 number ,我会考虑用 useMemo
Hoothin
2023-12-27 11:08:24 +08:00
不如直接寫成 class + shouldComponentUpdate 了
paledream
2023-12-27 11:15:49 +08:00
`这样岂不是可以把最外层的数据 rootData 做个 useState 就行了,里面各个组件从这个 rootData 里面拿.就是不知道对性能有没有影响了.`
1. 可以的
2. 对性能是有影响的
ZZITE
2023-12-27 11:18:48 +08:00
应该根据你的需求来决定,这个 otherNumber 需不需要自主更新(主动的去 set 他的值)?如果只是完全依赖于 number 值得变化而变化,那么这样写没什么问题,也不需要 memo 比较,比较也需要开销的。
panxiuqing
2023-12-27 11:30:44 +08:00
`otherNumber` 只是依赖 `number` 的话就不应该定义两个 state 。
state 是应用外部通知内部状态变化的入口,effect 是应用内部状态变化需要通知外部时的入口。
只由内部状态计算而来的状态应该用 `useMemo`,不然无关状态导致的重渲染产生了重复计算。
vincenteof
2023-12-27 11:33:46 +08:00
这是 react 推荐的,避免多余的 useState ,让数据自己流动
xubingok
2023-12-27 11:36:53 +08:00
@lisongeee 我错了..确实是开发模式下导致的~~~~
xubingok
2023-12-27 11:37:17 +08:00
@paledream 大佬,细说性能影响~~~
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
2023-12-27 11:38:55 +08:00
@ZZITE
@panxiuqing
感谢两位大佬解惑.
不过关于 useMemo 似乎有不同意见...这个东西我还要细看一下,目前确实只用到 state 和 effect 这两个 hook.
xubingok
2023-12-27 11:41:12 +08:00
@Yvette 感谢感谢..
那我就渲染时直接计算了,搞这么多 useState 还要挨个调用 setXXX 确实挺烦的.
codehz
2023-12-27 11:59:03 +08:00
@xubingok useMemo 是给计算需要一定成本的用的,简单加减乘除甚至字符串连接都不需要。
我觉得你这个例子里用 setState 没有问题,但是扩展到更大项目里,如果每个子元素的更新都要触发 root 组件的更新的话,就有点问题了,这时候可以考虑引入第三方状态管理库来处理
冷知识好多第三方状态管理库也都是接入到 react 的 useState (或者更高级的 useSyncExternalStore )来处理,但是针对复杂嵌套状态做了优化,避免了“牵一发动全身”的问题,只会在用到状态的地方触发 setState (或者 sync external store 里的回调)
xubingok
2023-12-27 14:27:52 +08:00
@codehz 谢谢.
xubingok
2023-12-27 14:39:54 +08:00
@Yvette
我又看了一遍 https://zh-hans.react.dev/learn/choosing-the-state-structure#avoid-duplication-in-state
确实有提到这个用法,写得是真好啊~
大佬实在太实诚了,这个章节正好解答了我的问题~~~
再次感谢~

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/1003740

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX