0

I've started to brush up on React after a long time away. Pretty much I have a "list" that stores companies interview processes. This is built by 2 react components. Is the list that aggregates each job.

When you go to "remove row" react registers the correct "row" to delete, (and by using a debugging simple case this happens) but it will not successfully update the inner component.

I've spent time researching this, and I've added a simple component called "Welcome." This helps me because I can use this to validate that I am removing the correct element, just the inner "jobrow" component is not updating correctly.

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>

    );
  }
}

I would like for the correct row that is removed to be reflected in the text boxes.

I'd really appreciate your feedback.

2 Answers 2

0

You need to give each item in an array of elements a key (you should be getting a console warning about that). And the key should be a unique identifier, NOT the array index. In your code pen, instead of

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

try

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

Then it correctly deletes row you're selecting. And research why using array indices as a key (or why not using keys at all for arrays of components) causes problems in React.

Sign up to request clarification or add additional context in comments.

Comments

0

Use getDerivedStateFromProps to update state in your 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>

Another option is to access the props directly in your JobsRow instead of saving them in state.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.