【问题标题】: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) 中修改成功,但从页面中删除的文本框始终是最后添加的,而不是应该删除的。例如:

  1. 点击“添加项目”,输入:aaa items: ['aaa']
  2. 点击“添加项目”,输入:bbb items: ['aaa', 'bbb']
  3. 点击“添加项目”,输入:ccc items: ['aaa', 'bbb', 'ccc']
  4. 点击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 - 我更新了代码,但我仍然遇到同样的问题。

标签: javascript reactjs react-native


【解决方案1】:

您的代码存在一些问题:

  1. 首先,您直接更改this.state,而不使用changeItem函数中的this.setState(),我已将其更改为var items = [...this.state.items];
  2. 您正在将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.htmlhttps://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318
  3. 我已经从Col 组件中删除了column 属性,因为它给出了一些警告
  4. 因为,现在我要为每个列表项添加一个唯一的 id,我已将 this.state.items 的结构从 array of strings 更改为 array of objects, where each object has a id and data, where data is text
  5. 您在使用 Form.Control 组件时没有使用 value 属性。假设您添加了一个列表项,在输入中写了一些内容,再次单击add item 按钮。此时,由于您更改了焦点,onBlur 事件将触发,您的this.state.items 将相应更改,到目前为止,非常好。但是现在当它再次重新渲染整个东西时,它将重新渲染 Form.Control 组件,但是没有 value 道具,这个组件将不知道要显示什么数据,因此它将呈现为空字段.因此,我在这个组件中添加了 value 属性
  6. 由于我在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;

【讨论】:

  • 我相信函数也需要绑定。
猜你喜欢
  • 2020-10-07
  • 2017-03-24
  • 1970-01-01
  • 2017-12-23
  • 2023-01-04
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 2022-07-08
相关资源
最近更新 更多