react immutable 相关困惑 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yukinotech
V2EX    React

react immutable 相关困惑

  •  
  •   yukinotech 2022-04-22 03:59:02 +08:00 2369 次点击
    这是一个创建于 1269 天前的主题,其中的信息可能已经有所发展或是发生改变。

    主要困惑的内容在两个 onClick 事件里,一个对于 state 是 immutable 的,另一个不是,但是效果是相同的。疑惑的点在于

    1. 两种写法是否都可以,第一种写法会不会导致 bug(比如 react18 cm 模式下)
    2. 如果两个写法都可以,是不是说明 react 本身并不强依赖 state 的 immutable ,只需要让 newState!==oldState ,也就是引用改变(非 js 基础类型),然后 newState 丢到 setState 里面去 react 都能正常 render
    import { useState } from "react" function App() { let [personList, setPersonList] = useState([{ name: "jack", age: 18 }]) const onClick1 = () => { let newState = [...personList] newState.push({ name: "kk", age: 1 }) // 这里相当于直接修改了 personList[0].name 上的值,对于 personList 这个 state 没有做到 immutable newState[0].name = "ddddddd" setPersonList(newState) } const onClick2 = () => { setPersonList([ { // 这里先复制一遍 personList[0],再复制,personList 这个 state 在过程中是只读的,是 immutable ...personList[0], name: "ddddddd", }, ...personList.slice(1), { name: "kk", age: 1 }, ]) } return ( <div className="App"> <button OnClick={onClick1}>add1</button> <button OnClick={onClick2}>add2</button> {personList.map((item, index) => { return <div key={index}>{item.name}</div> })} </div> ) } export default App 

    补充一下上述语境中 immutable 的定义,以常见的 immutable 库 immer 为例,可以看到执行函数,返回 new1 后,old 的值是不变的,这样可以认为 old 是 immutable 的

    import { produce } from "immer" const old = [{ name: "jack", age: 18 }] const new1 = produce(old, (state) => { state[0].name = "ddddddd" state.push({ name: "kk", age: 1 }) }) console.log("原始值", old) // 原始值 [ { name: 'jack', age: 18 } ] console.log("新值", new1) // 新值 [ { name: 'ddddddd', age: 18 }, { name: 'kk', age: 1 } ] 
    第 1 条附言    2022-04-22 15:33:59 +08:00

    1,2楼的回复应该是对的,我这补一下示例。Person1是PureComponent,Person是fc component,当直接传p.person时,按示例中的用法,p.person的引用对于2个props是没变的,PureComponent在这种情况下就不会刷新值。

    这一方面说明确实和实际的用法有关,另一方面我觉得也可以说明react本身和immutable是没啥关系的

    import React, { useState } from 'react' class Person1 extends React.PureComponent { constructor(props) { super(props) } render() { return <div>{this.props.person.name}</div> } } function Person({ person }) { return <div>{person.name}</div> } function App() { let [p, setP] = useState({ person: { name: 'jack', age: 18 }, }) const onClick1 = () => { let newState = { ...p } newState.person.name = 'kk' setP(newState) } return ( <div className="App"> <button OnClick={onClick1}>add1</button> {/* <button OnClick={onClick2}>add2</button> */} <Person person={p.person} /> <Person1 person={p.person} /> </div> ) } export default App 
    第 2 条附言    2022-04-22 16:02:14 +08:00
    再补一条
    class 中会有这个问题的是 PureComponent ,FC 中对标的应该就是 React.memo(),这下就对上了
    chnwillliu
        1
    chnwillliu  
       2022-04-22 04:12:20 +08:00 via Android   1
    是否依赖 state 的 immutability 完全取决于你的 state 在 view 中具体是如何使用的。

    你写一个 pure component 接受 person 对象作为 props, 然后放到你的 personList.map 里 render 试试。
    Yvette
        2
    Yvette  
       2022-04-22 07:57:43 +08:00   1
    没看过源码,但按我的理解 setState 只是去 trigger 了一次 re-render ,只有 Pure Component 才会需要用到 immutability ,可以试试楼上说的验证一下。
    gogogo1203
        3
    gogogo1203  
       2022-04-22 08:24:54 +08:00
    react 做 shallow comparison
    let newState = [...personList] , 你以为你造出了一个 new object, 其实 spreading 是 shallow copy 。nested object 里面的还是同一个 referrence.
    gogogo1203
        4
    gogogo1203  
       2022-04-22 08:25:54 +08:00
    如果是 nested object, 能用 immer 就用 immer 。 简单,不容易出现奇奇怪怪的 bug.
    yazoox
        5
    yazoox  
       2022-04-22 08:49:24 +08:00
    @gogogo1203 immer 会帮助深拷贝 nested object?
    gogogo1203
        6
    gogogo1203  
       2022-04-22 09:18:25 +08:00
    @yazoox 用 Immer 你就不用担心这问题了。还有个老办法就是 obj = JSON.parse(JSON.stringify(o)) , lodash 也有深拷贝的 func, lodash 还有 isEqual deep compare.
    jjwjiang
        7
    jjwjiang  
       2022-04-22 10:56:36 +08:00
    用 hooks 模式的时候,setState 以外的方法能够修改到真正的 state 吗?

    说实话我觉得没必要纠结这一点,setState 之后就变成了新的 state
    yukinotech
        8
    yukinotech  
    OP
       2022-04-22 14:53:50 +08:00
    @gogogo1203 你说的道理是对的。Shallow comparison 就是用 === 比较,对象就比较引用,所以浅复制一个 obj ,虽然不能改变 obj 属性里面的引用,但是浅复制后的新 obj 引用就不是原来的了,丢入 setState 能触发新的 render 。这个过程和对象的属性是同一个 referrence 没什么关系吧。而且 onClick1 的注释也说得比较清楚了,不然为啥说 newState[0].name = "ddddddd"就是修改 personList[0].name
    yukinotech
        9
    yukinotech  
    OP
       2022-04-22 14:57:23 +08:00
    @jjwjiang 纠结主要有 2 点。1. state.xx = xx 这个操作本身,会不会导致 bug 2.因为一直有人把 react 和 immutable 绑定到一起,所以我想弄清楚对 react 的 render 来说 immutable 是必须的吗,还是只需要保证 newState!==oldState 即可
    iseki
        10
    iseki  
       2022-04-22 15:33:53 +08:00 via Android
    你把 mutable 的东西当 prop 传给 children 时就容易出问题了,想想下 children 里面 useEffect(...,[theObject])
    yukinotech
        11
    yukinotech  
    OP
       2022-04-22 15:44:43 +08:00
    @iseki 嗯,有道理,所以还是和实际场景相关。这种场景 useEffect 依赖项可能就不会写 theObject ,而是 theObject.xxx ,或者上层组件 setState 的时候,自己{...一遍},再传给子组件。

    但是确实和 1 ,2 楼说的,Pure Component 才会引起渲染的问题,附言 1 里补充了一下
    AyaseEri
        12
    AyaseEri  
       2022-04-22 19:41:23 +08:00
    核心: React 做 diff 依赖数据变化(基本类型数据变化、对象引用变化)。
    所以你的数据是不是 immutable ,React 其实不关心,但是你做出不改引用改内容的行为时,React 不对更新做保证。你可能被网上某些文章给误导了。
    gogogo1203
        13
    gogogo1203  
       2022-04-22 19:45:35 +08:00
    @yukinotech deeply nested obj ......................................... [aa:{ bb:[cc,ee]}] 你用 spreading 是浅拷贝,bb 还是原来的 obj. 你可能是没有碰过那些奇奇怪怪的 bug. reference vs value .
    xiaoming1992
        14
    xiaoming1992  
       2022-04-22 20:28:06 +08:00
    如果是我的话,就不会用 personList ,而是 setPersonList((prevList) => xxx)
    ChefIsAwesome
        15
    ChefIsAwesome  
       2022-04-22 20:44:40 +08:00   1
    react 不来就不依赖 immutable 那些东西。
    正常的机制就是你 setState 之后,react 新旧 state 做一遍 deep diff 。react 有 shouldComponentUpdate 这个 api ,让你可以自己处理 diff 机制。
    当初 react 面世之后,Clojure 有个基于 react ,叫 om 的项目。因为 Clojure 是函数式编程,数据是 immutable 的,在 shouldComponentUpdate 里比较两个 immutable 就是 === 这么判断,这就大大提高了运行速度。接下来就出现了各种用 js 实现 immutable 的库。函数式编程也在 js 圈子开始流行起来。
    yukinotech
        16
    yukinotech  
    OP
       2022-04-22 20:59:48 +08:00
    @ChefIsAwesome 这也是困惑的点,因为网上现在一谈 react 就喜欢讲 immutable ,但是我自己实践就觉得 react 本身是不依赖 immutable 那些东西的,所以发个贴,问一下是不是我想错了。。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2602 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 05:12 PVG 13:12 LAX 22:12 JFK 01:12
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86