在React.js中删除项目时出现意外行为

时间:2019-05-18 23:44:25

标签: reactjs

很长一段时间后,我开始重新学习React。我几乎有一个“列表”,用于存储公司面试过程。这是由2个React组件构建的。是汇总每个作业的列表。

当您转到“删除行”时,react寄存器将删除正确的“行”(并且通过使用调试简单的情况发生),但是不会成功更新内部组件。

我花了很多时间研究这个问题,并且添加了一个简单的组件“ Welcome”。这对我有帮助,因为我可以使用它来验证是否删除了正确的元素,只是内部的“作业”组件未正确更新。

https://codepen.io/anon/pen/XwaWPj

class Jobs extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "",
      jobs: props.data.items
      //jobs: [{ id: "" }]
    };
  }

  handleAddJob = () => {
    this.setState({
      jobs: this.state.jobs.concat([{ "company":"", "position": "", "next_steps": []}])
    });
    console.log(this.state);
  };

  handleRemoveJob = key => () => {
    //var index = this.state.jobs.indexOf(items)
    console.log(this.state.jobs.filter((item, j) => item.key !== key) )
    this.setState({
      //shareholders: this.state.shareholders.filter((s, sidx) => idx !== sidx)
      //next_steps: this.state.next_steps.splice(idx, 1)
      jobs: this.state.jobs.filter((item, j) => item.key !== key)
    });
  };

  //<JobRow
  //    company={items.company}
  //    position={items.position}
  //    next_steps={items.next_steps}/>
  render() {
    return (
    <div>
    <h4>Jobs Applied</h4>

      {this.state.jobs.map((items =>
      <div>
        <Welcome name={items.company} />
        <JobRow
            company={items.company}
            position={items.position}
            next_steps={items.next_steps}/>
        <button
          type="button"
          onClick={this.handleRemoveJob(items.key)} //.bind(this)
          className="small">
          remove row
        </button>
      </div>
    ))
    }

    <button
      type="button"
      onClick={this.handleAddJob}
      className="small">
      Add New Job
    </button>
    </div>
  )
  };
}
// ===========
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// ===========
//https://stackoverflow.com/questions/50147840/how-to-format-and-display-json-data-using-array-map-in-reactjs
class JobRow extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      company: props.company,
      position: props.position,
      next_steps: props.next_steps,
    };
  }

  handleNameChange = evt => {
    this.setState({ name: evt.target.value });
  };

  handleAddField = () => {
    this.setState({
      //shareholders: this.state.shareholders.concat([{ name: "" }])
      next_steps: this.state.next_steps.concat("")
    });
  };

  handleRemoveField = idx => () => {
    this.setState({
      //shareholders: this.state.shareholders.filter((s, sidx) => idx !== sidx)
      //next_steps: this.state.next_steps.splice(idx, 1)
      next_steps: this.state.next_steps.filter((s, sidx) => idx !== sidx)
    });
  };

  changeTextCompany(event){
        this.setState(
            //"{this.state.textValue : event.target.value}"
            {company: event.target.value}
        );
    }

  render() {
    return (
    <div>
        <div class="flex-container">
          <div class="inner_flex">
            <span>
              <input type="text" class="form-control" placeholder="Company" value={this.state.company} id="comapny_input" onChange={this.changeTextCompany}/>
            </span>
            <span>
              <input type="text" class="form-control" placeholder="Position" value={this.state.position} oninput="selectJobType()" id="position_input"/>
            </span>
            <span>
              <select id="position_type">
                <option value="fulltime">Fulltime</option>
                <option value="intern">Co-Op/Internship</option>
              </select>
            </span>
            </div>

        {this.state.next_steps.map((step, idx) => (
            <span>
            <button
              type="button"
              onClick={this.handleRemoveField(idx)}
              className="small"
            >
              -
            </button>
            <input placeholder="Next State" value={step} />
            </span>

        ))}

      <button
        type="button"
        onClick={this.handleAddField}
        className="small">
        Next Stage
      </button>
    </div>
        </div>

    );
  }
}

我希望删除的正确行反映在文本框中。

非常感谢您的反馈。

2 个答案:

答案 0 :(得分:0)

您需要为元素数组中的每个项目赋予一个键(您应该得到关于此的控制台警告)。键应该是唯一的标识符,而不是数组索引。用您的密码笔代替

{this.state.jobs.map((items =>
    <div>

尝试

{this.state.jobs.map((items =>
    <div key={items.key}>

然后正确删除您选择的行。并研究为什么将数组索引用作键(或者为什么不对组件数组完全使用键)在React中引起问题。

答案 1 :(得分:0)

使用getDerivedStateFromProps更新JobsRow中的状态。

.flex-container {
  display: flex;
  background-color: #f1f1f1;
}

.flex-container > div {
  background-color: #B6E3DC;
  margin: 0px;
  padding: 5px;
}

.flex-container > div > span {
    display: inline-block;
    padding: 2.5px;
}

input {display: block !important; padding: 0 !important; margin: 0 !important; border: 0 !important; width: 100% !important; border-radius: 0 !important; line-height: 1 !important;}
td {margin: 0 !important; padding: 0 !important;}

input {display: block !important; padding: 0 !important; margin: 0 !important; border: 0 !important; width: 100% !important; border-radius: 0 !important; line-height: 1 !important;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>

<script type="text/babel">

var json = {"items":[
  {"key":132, "company":"Google", "position": "SE Intern", "next_steps": ["Coding", "phone"]
  },


  {"key":133, "company":"FaceBook", "position": "DS Intern", "next_steps": ["Onsite", "Offer"]
  },
  {"key":134, "company":"twitter", "position": "architectre", "next_steps": ["coffeechat", "denail"]
  },
  {"key":135, "company":"oracle", "position": "sleeping", "next_steps": []
  }
]}

class Jobs extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: '',
      jobs: this.props.data.items
      //jobs: [{ id: "" }]
    };
  }

  handleAddJob = () => {
    this.setState({
      jobs: this.state.jobs.concat([
        { company: '', position: '', next_steps: [] }
      ])
    });
    console.log(this.state);
  };

  handleRemoveJob = key => () => {
    //var index = this.state.jobs.indexOf(items)
    //console.log(this.state.jobs.filter((item, j) => item.key !== key));
    this.setState({
      //shareholders: this.state.shareholders.filter((s, sidx) => idx !== sidx)
      //next_steps: this.state.next_steps.splice(idx, 1)
      jobs: this.state.jobs.filter((item, j) => item.key !== key)
    });
  };

  //<JobRow
  //    company={items.company}
  //    position={items.position}
  //    next_steps={items.next_steps}/>
  render() {
    return (
      <div>
        <h4>Jobs Applied</h4>

        {this.state.jobs.map(items => (
          <div>
            <Welcome name={items.company} />
            <JobRow
              company={items.company}
              position={items.position}
              next_steps={items.next_steps}
            />
            <button
              type="button"
              onClick={this.handleRemoveJob(items.key)} //.bind(this)
              className="small"
            >
              remove row
            </button>
          </div>
        ))}

        <button type="button" onClick={this.handleAddJob} className="small">
          Add New Job
        </button>
      </div>
    );
  }
}
// ===========
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// ===========
//https://stackoverflow.com/questions/50147840/how-to-format-and-display-json-data-using-array-map-in-reactjs
class JobRow extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      company: props.company,
      position: props.position,
      next_steps: props.next_steps
    };
  }


  static getDerivedStateFromProps(props, state) {
    // compare props with state data
    // if they are not equal return props
    // or return null
    // more info here https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
    return props;
  }

  handleNameChange = evt => {
    this.setState({ name: evt.target.value });
  };

  handleAddField = () => {
    this.setState({
      //shareholders: this.state.shareholders.concat([{ name: "" }])
      next_steps: this.state.next_steps.concat('')
    });
  };

  handleRemoveField = idx => () => {
    this.setState({
      //shareholders: this.state.shareholders.filter((s, sidx) => idx !== sidx)
      //next_steps: this.state.next_steps.splice(idx, 1)
      next_steps: this.state.next_steps.filter((s, sidx) => idx !== sidx)
    });
  };

  changeTextCompany(event) {
    this.setState(
      //"{this.state.textValue : event.target.value}"
      { company: event.target.value }
    );
  }

  render() {
    return (
      <div>
        <div class="flex-container">
          <div class="inner_flex">
            <span>
              <input
                type="text"
                class="form-control"
                placeholder="Company"
                value={this.state.company}
                id="comapny_input"
                onChange={this.changeTextCompany}
              />
            </span>
            <span>
              <input
                type="text"
                class="form-control"
                placeholder="Position"
                value={this.state.position}
                oninput="selectJobType()"
                id="position_input"
              />
            </span>
            <span>
              <select id="position_type">
                <option value="fulltime">Fulltime</option>
                <option value="intern">Co-Op/Internship</option>
              </select>
            </span>
          </div>

          {this.state.next_steps.map((step, idx) => (
            <span>
              <button
                type="button"
                onClick={this.handleRemoveField(idx)}
                className="small"
              >
                -
              </button>
              <input placeholder="Next State" value={step} />
            </span>
          ))}

          <button type="button" onClick={this.handleAddField} className="small">
            Next Stage
          </button>
        </div>
      </div>
    );
  }
}
ReactDOM.render(<Jobs data={json}/>, document.getElementById('root'));
</script>

另一种选择是直接在JobsRow中访问道具,而不是将其保存为状态。

<span>
  <input
   ...
    value={this.props.company}
   ...
  />
</span>
<span>
  <input
    ...
    value={this.props.position}
    ...
  />
</span>