状态更改后反应无法正确渲染

Posted

技术标签:

【中文标题】状态更改后反应无法正确渲染【英文标题】:React not rendering correctly after state change 【发布时间】:2022-01-18 00:33:23 【问题描述】:

我有一个简单的表格。您单击“添加项目”按钮并出现一个文本框。模糊时,在文本框中输入的文本被添加到状态变量数组中。再次单击“添加项目”按钮,出现另一个文本框,依此类推。

对于每个文本框,还有一个“删除项目”按钮。单击此按钮时,当前项从数组中移除,当前文本框从页面中移除。

class App extends React.Component 
 constructor(props) 
   super(props);
   this.state = 
     items: []
   
 

 addItem() 
  this.setState(        
    items: [...this.state.items, []]
    
  )


removeItem(index) 
  //var items = this.state.items;
  var items = [...this.state.items];

  items.splice(index, 1);

  this.setState(
    items: items
  )


changeItem(e, index) 
  var items = this.state.items;  

  items[index] = e.target.value;

  this.setState(
    items: items
  )


  render() 
    return (
      <div>
        
          this.state.items.map((item, index) => 
            return (
              <React.Fragment key=index>
                <hr />
                <Row>
                    <Col column sm="8">
                      <Form.Control
                        type="text"
                        name="item"                                              
                        onBlur=(e) => this.changeItem(e, index)
                      />
                    </Col>
                  </Row>
                  <Row>
                    <Col column sm="8">
                      <Button
                        onClick=() => this.removeItem(index)
                        variant="link"
                        size="sm">
                          Remove Item
                      </Button>                                                            
                    </Col>                              
                  </Row>         
              </React.Fragment>
            )
          )
        
        <br />
          <Button
            onClick=(e) => this.addItem(e)
            variant="outline-info">Add item
          </Button>
      </div>
    )
  

我遇到的问题是,虽然数组在removeItem(index) 中修改成功,但从页面中删除的文本框始终是最后添加的,而不是应该删除的。例如:

    点击“添加项目”,输入:aaa items: ['aaa'] 点击“添加项目”,输入:bbb items: ['aaa', 'bbb'] 点击“添加项目”,输入:ccc items: ['aaa', 'bbb', 'ccc'] 点击aaa下的“删除项目”。项目成功更新:items: ['bbb', 'ccc']

页面应该显示一个带有 bbb 的文本框和一个带有 ccc 的文本框。但它显示:

如何从页面中删除正确的文本框?

【问题讨论】:

你不应该在removeItem函数中使用splice,因为splice改变了它正在处理的数组,所以基本上你在不使用setState函数的情况下改变了状态,这是不正确的在反应。相反,您应该使用[...this.state.items] 创建一个具有相同内容的新数组,然后在该新数组上使用splice,然后使用该新数组设置状态。 ` items: [...this.state.items, []]` 添加的项目看起来不正确。 ` items: [...this.state.items, '']` .其他一切看起来都很好 尝试在文本框中添加一个键 @RishabhGupta - 我确实在 removeItem() 中创建了一个新数组。我没有直接修改状态。如果你打算在 addItem() 中这样做,我试过了,但我仍然遇到同样的问题。 @RishabhGupta - 我更新了代码,但我仍然遇到同样的问题。 【参考方案1】:

您的代码存在一些问题:

    首先,您直接更改this.state,而不使用changeItem函数中的this.setState(),我已将其更改为var items = [...this.state.items]; 您正在将index 用作列表项的key,并使用&lt;React.Fragment key=index&gt; 中的this.state.items.map((item, index) =&gt; ...) 进行渲染。这个key 应该是列表项的唯一标识符,通常是数据库中唯一的idKeys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity。但是,在您的情况下,由于您没有项目的唯一 ID,因此我正在使用 uuid 模块创建这些项目。了解更多关于按键的信息:https://reactjs.org/docs/lists-and-keys.html 和 https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318 我已经从Col 组件中删除了column 属性,因为它给出了一些警告 因为,现在我要为每个列表项添加一个唯一的 id,我已将 this.state.items 的结构从 array of strings 更改为 array of objects, where each object has a id and data, where data is text 您在使用 Form.Control 组件时没有使用 value 属性。假设您添加了一个列表项,在输入中写了一些内容,再次单击add item 按钮。此时,由于您更改了焦点,onBlur 事件将触发,您的this.state.items 将相应更改,到目前为止,非常好。但是现在当它再次重新渲染整个东西时,它将重新渲染 Form.Control 组件,但是没有 value 道具,这个组件将不知道要显示什么数据,因此它将呈现为空字段.因此,我在这个组件中添加了 value 属性 由于我在Form.Control 组件中添加了value 属性,因此现在反应要求我将onChange 事件添加到组件中,否则它将呈现为只读输入,因此我将onBlur 更改为onChange事件。当onChange 已经存在时,onBlur 无需更改状态值。

这是完成的代码:

import React from "react";
import  v4  from 'uuid';
import  Button, Row, Col, Form  from "react-bootstrap";

class App extends React.Component 
  constructor(props) 
    super(props);
    this.state = 
      items: []
    ;
  

  addItem() 
    this.setState(
      items: [...this.state.items, data: "", id: v4()]
    );
  

  removeItem(index) 
    console.log("dbg1", index);
    //var items = this.state.items;
    var items = [...this.state.items];

    items.splice(index, 1);

    this.setState(
      items: items
    );
  

  changeItem(e, index) 
    console.log("dbg2", this.state.items);
    var items = [...this.state.items];

    items[index].data = e.target.value;

    this.setState(
      items: items
    );
  

  render() 
    console.log("dbg3", this.state.items);
    return (
      <div>
        this.state.items.map((item, index) => 
          return (
            <React.Fragment key=item.id>
              <hr />
              <Row>
                <Col sm="8">
                  <Form.Control
                    type="text"
                    name="item"
                    value=item.data
                    onChange=(e) => this.changeItem(e, index)
                    // onBlur=(e) => this.changeItem(e, index)
                  />
                </Col>
              </Row>
              <Row>
                <Col sm="8">
                  <Button
                    onClick=() => this.removeItem(index)
                    variant="link"
                    size="sm"
                  >
                    Remove Item
                  </Button>
                </Col>
              </Row>
            </React.Fragment>
          );
        )
        <br />
        <Button onClick=(e) => this.addItem(e) variant="outline-info">
          Add item
        </Button>
      </div>
    );
  


export default App;

【讨论】:

我相信函数也需要绑定。

以上是关于状态更改后反应无法正确渲染的主要内容,如果未能解决你的问题,请参考以下文章

组件不会重新渲染,但 redux 状态已通过反应钩子更改

将新的商店状态放入道具后,反应不会重新渲染

页面重新渲染/更改后如何维护组件状态?

React Native:状态更改后无法重新渲染图像

调用 React Setstate 回调但延迟渲染

setState 更改状态但不重新渲染