React教程,第17部分:处理TODO应用程序的第五阶段,修改组件状态

在今天的React课程翻译部分中,我们建议您完成下一个实际任务,并向您介绍有关如何修改React组件状态的故事。

图片

第1部分:课程概述,React,ReactDOM和JSX普及的原因
第2部分:功能组件
第3部分:组件文件,项目结构
第4部分:父级和子级组件
第5部分:TODO应用程序的开始工作,样式设计的基础
第6部分:关于本课程的一些功能,JSX和JavaScript
第7部分:内联样式
第8部分:继续研究TODO应用程序,熟悉组件的属性
第9部分:组件属性
第10部分:使用组件特性和样式的研讨会
第11部分:动态标记生成和映射数组方法
第12部分:研讨会,TODO应用程序的第三阶段工作
第13部分:基于类的组件
第14部分:关于基于类的组件,组件状态的研讨会
第15部分:组件健康研讨会
第16部分:TODO应用程序的第四阶段工作,事件处理
第17部分:TODO应用程序的第五阶段工作,修改组件的状态
第18部分:TODO应用程序的第六阶段工作
第19部分:组件生命周期方法
第20部分:条件渲染的第一课
第21部分:关于条件渲染的第二课​​和研讨会
第22部分:TODO应用程序的第七阶段工作,从外部资源下载数据
第23部分:关于使用表格的第一课
第24部分:第二形式课
第25部分:使用表单的研讨会
第26部分:应用程序体系结构,容器/组件模式
第27部分:课程项目

课31.讲习班。 TODO应用程序。 阶段数5


原创

▍工作


启动Todo应用程序时,您可能会注意到控制台中显示了一条通知,指示我们已经在TodoItem组件中配置了元素的checked属性,但没有提供与该元素进行交互的机制作为onChange事件onChange 。 使用应用程序界面时,这导致无法显示或取消选中页面上显示的标志。

在这里,您被邀请为TodoItem组件的checkbox类型的元素配备一个TodoItem事件处理程序,在当前工作阶段,该事件处理程序足以以将某些内容输出到控制台的函数的形式呈现。

▍解决方案


这是TodoItem组件代码现在的样子,存储在TodoItem.js文件中:

 import React from "react" function TodoItem(props) {   return (       <div className="todo-item">           <input type="checkbox" checked={props.item.completed}/>           <p>{props.item.text}</p>       </div>   ) } export default TodoItem 

这是启动应用程序时控制台显示的内容。


控制台通知

同时,这些标志不响应我们的效果。

为了摆脱此通知并为进一步的工作做好准备,只需将一个onChange事件onChange分配给checkbox元素就足够了。 这是代码中的样子:

 import React from "react" function TodoItem(props) {   return (       <div className="todo-item">           <input               type="checkbox"               checked={props.item.completed}               onChange={() => console.log("Changed!")}           />           <p>{props.item.text}</p>       </div>   ) } export default TodoItem 

在这里,作为处理程序,我们使用一个简单的函数将单词Checked!输出到控制台Checked! 。 同时,单击标志不会导致其状态发生变化,但是来自控制台的通知(如下图所示)消失了。


标记仍无法正常工作,但来自控制台的通知消失了

对应用程序进行的微小更改将使我们在处理组件状态的更改之后,可使复选框正确运行。

课程32.更改组件的状态


原创

让我们从在包含以下代码的App.js文件中使用create-react-app的标准应用程序开始:

 import React from "react" class App extends React.Component {   constructor() {       super()       this.state = {           count: 0       }   }     render() {       return (           <div>               <h1>{this.state.count}</h1>               <button>Change!</button>           </div>       )   } } export default App 

index.js文件中包含的index.css样式index.css ,包含以下样式描述:

 div {   display: flex;   flex-direction: column;   align-items: center;   justify-content: center; } h1 {   font-size: 3em; } button {   border: 1px solid lightgray;   background-color: transparent;   padding: 10px;   border-radius: 4px;  } button:hover {   cursor: pointer; } button:focus {   outline:0; } 

在此阶段,该应用程序如下图所示。


浏览器中的应用程序页面

今天,我们将讨论如何更改组件的状态。 如果组件具有状态,则可以通过对其进行初始化来在其中存储一些数据。 但是,如果状态无法更改,则组件不会因其存在而特别受益,其中的数据存储与例如在组件代码中硬拷贝它们不会有很大的不同。

让我们讨论一下该应用程序,在该示例中,我们将考虑使用组件的状态。 上面提供了代码的App组件是基于类的组件。 这很明显,因为我们需要该组件具有状态。 在组件代码中,我们使用构造函数。

在其中,我们一如既往地调用super()方法,并通过将count属性写入状态并为其分配初始值0来初始化状态。 在render()方法中,我们打印一个表示组件状态中count属性值的第一级标题,以及带有单词Change!的按钮Change! 。 所有这些都使用样式设置格式。

如果在此应用程序的工作阶段,在浏览器中将其打开并单击按钮,则当然不会发生任何事情。 但是我们需要单击按钮以更改组件的状态,从而影响其count属性。 同时,我们已经研究了在React中处理事件的方法,我们的任务是创建一种机制,该机制响应按钮的单击,更改count的state属性。

让我们开始解决我们的问题,为按钮配备一个onClick事件onClick ,对于初学者而言,该事件onClick将仅向控制台输出一些内容。

为此,我们将向组件类添加一个新方法。 您可以随意调用它,但是习惯上调用此类方法,以便它们的名称可以指示它们正在处理的事件。 结果,由于我们将使用它来处理click事件,因此将其称为handleClick() 。 这是App组件代码现在的样子。

 import React from "react" class App extends React.Component {   constructor() {       super()       this.state = {           count: 0       }   }     handleClick() {       console.log("I'm working!")   }     render() {       return (           <div>               <h1>{this.state.count}</h1>               <button onClick={this.handleClick}>Change!</button>           </div>       )   } } export default App 

请注意,从render()引用此方法,我们使用this.handleClick形式的this.handleClick

现在,如果您单击按钮,相应的消息将出现在控制台中。


单击按钮将调用类方法。

现在,使它成为可能,以便单击该按钮将增加其上方显示的数字,即修改组件的状态。 也许尝试在handleClick()方法中直接更改组件的状态? 假设如果我们这样重写此方法:

 handleClick() {   this.state.count++ } 

我必须马上说,这不适用于React中的组件状态。 尝试执行此类代码将引发错误。

组件的状况可以与人穿的衣服进行比较。 如果他想换衣服,他不会在不脱衣服的情况下改变或重新粉刷衣服,而是脱下她并穿上别的东西。 实际上,这正是它们如何处理组件状态。

您可能还记得我们曾经讨论过一种用于修改状态的特殊方法,该方法在基于类的组件中可用,因为它们扩展了React.Component类。 这是setState()方法。 它用于需要更改组件状态的情况。 该方法可以以不同的方式使用。

回想一下状态是一个对象。 让我们尝试将一个将替换状态的对象传递给setState()方法。 我们这样重写handleClick()方法:

 handleClick() {   this.setState({ count: 1 }) } 

尝试使用此方法将导致以下错误: TypeError: Cannot read property 'setState' of undefined 。 实际上,我们现在谈论的内容在React开发人员中引起了很多争议,现在我将向您展示一种解决此问题的非常简单的方法,乍看之下似乎并不寻常。

关键是每次创建一个计划使用setState()方法的类方法(在我们的例子中为handleClick() setState() ,该方法都必须与this相关联。 这是在构造函数中完成的。 修改后的组件代码如下所示:

 import React from "react" class App extends React.Component {   constructor() {       super()       this.state = {           count: 0       }       this.handleClick = this.handleClick.bind(this)   }     handleClick() {       this.setState({ count: 1 })   }     render() {       return (           <div>               <h1>{this.state.count}</h1>               <button onClick={this.handleClick}>Change!</button>           </div>       )   } } export default App 

现在,点击Change!按钮Change! 数字1将出现在其上方,错误消息将不会显示。


按下按钮可修改状态

没错,该按钮原来是“一次性的”。 第一次单击后, 0变为1 ,如果再次单击它,则什么也不会发生。 通常,这不足为奇。 单击按钮时调用的代码会完成其工作,每次将状态更改为新状态,但是在第一次单击按钮后,在count属性中存储数字1的新状态与旧状态没有区别。 为了解决此问题,请考虑另一种使用setState()方法的方法。

如果我们对组件的先前状态不感兴趣,则可以简单地将一个对象传递给此方法,该方法将替换状态。 但是,经常会发生组件的新状态取决于旧状态的情况。 在我们的例子中,这意味着,基于存储在该状态的先前版本中的count属性的值,我们希望对该值加1。 如果要更改状态,则需要知道以前存储在状态中的内容,可以将setState()方法传递给一个函数,该函数作为参数接收状态的先前版本。 您可以根据需要命名此参数,在本例中为prevState 。 此功能的采购将如下所示:

 handleClick() {   this.setState(prevState => {             }) } 

您可能会认为,在这样的函数中,只需使用表单this.state的构造来引用状态就足够了,但是这种方法不适合我们。 因此,此功能接受组件状态的先前版本很重要。

该函数应返回该状态的新版本。 这是handleClick()方法handleClick()该问题的方式:

 handleClick() {   this.setState(prevState => {       return {           count: prevState.count + 1       }   }) } 

请注意,要获取count属性的新值,我们使用count: prevState.count + 1构造。 您可能会认为以下形式的count: prevState.count++构造count: prevState.count++ ,但是++运算符count: prevState.count++对其应用count: prevState.count++变量,这意味着尝试修改状态的先前版本,因此在此不使用它。

在此阶段,组件文件的完整代码将如下所示:

 import React from "react" class App extends React.Component {   constructor() {       super()       this.state = {           count: 0       }       this.handleClick = this.handleClick.bind(this)   }     handleClick() {       this.setState(prevState => {           return {               count: prevState.count + 1           }       })   }       render() {       return (           <div>               <h1>{this.state.count}</h1>               <button onClick={this.handleClick}>Change!</button>           </div>       )   } } export default App 

现在,每次单击按钮都会增加计数器值。


每次单击按钮都会增加计数器值。

我们刚刚发现的内容为我们在开发React应用程序中打开了巨大的机会。

前面我们说过,父组件可以通过属性机制将属性从其自身的状态传递给子组件。 如果React检测到父组件的状态发生了变化,它将重新渲染此状态传递到的子组件。 它看起来像是对render()方法的调用。 结果,子组件将反映以父组件状态存储的新数据。

总结


今天,您已经准备好Todo应用程序以进行进一步的工作,并且还熟悉了React中用于更改组件状态的机制。 下次,您将被要求使用今天学到的知识来扩展培训应用程序的功能。

亲爱的读者们! 您如何看待React中组件的状态,而无需使用特殊机制就无法直接更改这一事实?

Source: https://habr.com/ru/post/zh-CN439984/


All Articles