草庐IT

javascript - 如何在 React 中更新嵌套状态,状态应该是不可变的吗?

coder 2025-01-10 原文

我们正在热烈讨论如何在 React 中更新嵌套状态。 状态是否应该是不可变的?优雅地更新状态的最佳做法是什么?

假设您的状态结构如下所示:

this.state = { 
                numberOfStudents: "3",
                gradeLevel: "5",
                students : [ 
                    { id : "1234",
                      firstName: "John",
                      lastName: "Doe",
                      email: "johndoe@mail.com"
                      phoneNumer: "12345"

                    },
                    { id : "56789",
                      firstName: "Jane",
                      lastName: "Doe",
                      email: "janedoe@mail.com"
                      phoneNumer: "56789"
                    },
                    { id : "11111",
                      firstName: "Joe",
                      lastName: "Doe",
                      email: "joedoe@mail.com"
                      phoneNumer: "11111"
                    }
                ]
            }

然后我们要更新 joe doe 的电话号码。 我们可以通过几种方式做到这一点:

改变状态+强制更新重新渲染

this.state.students[2].phoneNumber = "9999999";
this.forceUpdate();

改变状态+改变状态的setState

this.state.students[2].phoneNumber = "9999999";
this.setState({
     students: this.state.students
});

Object.assign,这仍然会改变状态,因为 newStudents 只是对 this.state 指向的同一对象的新引用

const newStudents = Object.assign({}, this.state.students);
newStudents[2].phoneNumber = "9999999"
this.setState({
     students: newStudents
});

更新不变性助手 ( https://facebook.github.io/react/docs/update.html ) + setState。如果我们在每个学生对象中都有 address.street、address.city、address.zip 并且想要更新街道,这会很快变得很糟糕。

const newStudents = React.addons.update(this.state.students, {2: {phoneNumber: {$set:"9999999"}}});
this.setState({
     students: newStudents
})

setState 的 react 文档的最后一行指出:

Never mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable. https://facebook.github.io/react/docs/react-component.html

文档指出我们不应该使用 forceUpdate 来重新渲染:

Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render().

为什么会这样,如果我们改变状态然后调用 setState 会发生什么?在什么情况下 setState() 会替换我们所做的 mutation?这是一个非常令人困惑的说法。谁能解释一下我们在上面设置状态时使用的每个场景可能出现的复杂情况。

最佳答案

您声明:

“Object.assign,这仍然会改变状态,因为 newStudents 只是对 this.state 指向的同一对象的新引用”

此语句不正确
Object.assign 改变传入其第一个参数的状态。由于您传入了一个空对象文字 ({}),您正在改变新的对象文字 and 而不是 this.state.


一些背景:

不可变状态的原理与函数式编程有关。

It is useful in React because it provides a way for React to know if the state has changed at all, one use case it is useful is for optimising when components should re-render

考虑具有嵌套对象的复杂状态的情况。 改变状态的值会改变状态中的属性值,但不会改变对象的引用。

this.state = {nestObject:{nestedObj:{nestObj:'yes'}}};

// mutate state
this.state.nestObject.nestedObj.nestObj= 'no';

我们如何知道 React 是否应该重新渲染组件?

  1. 深度平等检查?想象一下这在一个复杂的状态下会是什么样子,每个状态更新都要进行数百次甚至数千次检查...
  2. 无需检查更改,只需强制 React 在每次状态更改时重新渲染所有内容...

是否可以替代后两种方法?


不可变方式

通过创建一个新对象(以及一个新引用),使用 Object.assign 复制旧状态并改变它,您可以操纵状态值并更改对象引用。

通过不可变状态方法,我们可以通过简单地检查对象引用是否相等来知道状态是否发生了变化。

下面评论中反对者的简化示例:

考虑这个简单的例子:

this this.state = { someValue: 'test'}
var newState = Object.assign({}, this.state);
console.log(newState);                  // logs: Object {someValue: "test"]  
console.log(this.state);                // logs: Object {someValue: "test"]

// logs suggest the object are equal (in property and property value at least...

console.log(this.state === this.state); // logs: true

console.log(this.state === newState);   // logs: false.  Objects are 
                                        // pass-by-reference, the values stored
                                        // stored in this.state AND newState
                                        // are references.  The strict equality
                                        // shows that these references
                                        // DON'T MATCH so we can see
                                        // that an intent to modify
                                        // state has been made

关于javascript - 如何在 React 中更新嵌套状态,状态应该是不可变的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42101171/

有关javascript - 如何在 React 中更新嵌套状态,状态应该是不可变的吗?的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  3. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  4. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  5. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  6. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  7. ruby - 将散列转换为嵌套散列 - 2

    这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[

  8. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  9. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  10. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

随机推荐