1

Currently a user can enter first, lastname, and employee id. When it submits it renders an iteration of employees with the first, last, and employee id.

The problem is when you click edit, it edits the data but it will edit all of the fields for every object in the employees iteration.

How can I fix the code so that it will edit just that particular object within that iteration.

Home component

........
class Login extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activeTab: '3',
      firstName: '',
      lastName: '',
      employeeID: '',
      employees: [],
      Edit: false,
      updatedFirst: '',
      updatedLast:'',
      updatedEmployeeID: ''
    };
  }
  toggle = (tab) => {
    if(this.state.activeTab !== tab){
      this.setState({
        activeTab: tab
      })
    }
  }
  onSubmit = (e) => {
      e.preventDefault();
      const {firstName, lastName, employeeID} = this.state
      const ourForm ={
          firstName: firstName,
          lastName: lastName,
          employeeID: employeeID,
        // we need an id to so that it can be edited properly
      }
      this.setState({
          employees: [...this.state.employees,ourForm]
      }, () => {
          console.log(this.state.employees)
      })
  }
  onChange = (e) => {
      e.preventDefault()
    // e.preventDefault();
    this.setState({
        [e.target.name]: e.target.value,
    });
  }
  updatedChange = (e) => {
    e.preventDefault()
  // e.preventDefault();
    this.setState({
        [e.target.name]: e.target.value,
    });
  }

  onEdit = (e) => {
      e.preventDefault();
      this.setState({
          Edit: !this.state.Edit
      })
  }

  onReset = (e) => {
      e.preventDefault();
      this.setState({
          firstName: '',
          lastName: '',
          employeeID: ''
      })
  }
  render(){
    return (
            ......
                <MyForm 
                    onSubmit={this.onSubmit} 
                    onChange={this.onChange}
                    onReset={this.onReset}
                    firstName={this.state.firstName}
                    lastName={this.state.lastName}
                    employeeID={this.state.employeeID}
                    />
              </Col> 
            </Row>
          </TabPane>
        </TabContent>
        </Container>

        <List
            employees={this.state.employees}
            Edit={this.state.Edit}
            onEdit ={this.onEdit}
            onChange={this.onChange}
            updatedEmployeeID={this.state.updatedEmployeeID}
            updatedFirst={this.state.updatedFirst}
            updatedLast={this.state.updatedLast}
            />
      </div>
    );
  }
}
export default Login;

Form.js

import React, {Component} from 'react';
import { Col, Form, FormGroup, Label, Input, Button } from 'reactstrap';
const MyForm = (props) => {
    return(
        <Form style={{ margin: '30px 0px'}}>
        <FormGroup row>
          <Label for="firstName"  sm={2} size="sm">First Name:</Label>
          <Col sm={10}>
            <Input type="text" onChange={props.onChange} value={props.firstName} name="firstName" id="exampleEmail" placeholder="Enter First Name"/>
          </Col>
        </FormGroup>
        <FormGroup row>
          <Label for="lastName" sm={2} size="sm">Last Name:</Label>
          <Col sm={10}>
            <Input type="text" onChange={props.onChange} value={props.lastName} name="lastName" id="exampleEmail2" placeholder="Enter Last Name" />
          </Col>
        </FormGroup>
        <FormGroup row>
          <Label for="Employee ID" sm={2} size="sm">Employee ID:</Label>
          <Col sm={5}>
            <Input type="text" onChange={props.onChange} value={props.employeeID} name="employeeID" id="exampleEmail2" placeholder="Enter Employee ID" />
          </Col>
        </FormGroup>
        <FormGroup row>
            <Col sm={12}>
                <div className="float-right">
                    <Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">Add</Button> 
                    <Button onClick={props.onReset} size="lg"  style={{ margin: '0px 5px'}}color="warning">Reset</Button>
                </div>
            </Col>
        </FormGroup>
        <hr></hr>
        <FormGroup row>
            <Col sm={4}>
                <Input type="text"  name="search" id="exampleEmail2" placeholder="Search" />   
            </Col>

            <Col sm={8}>
                <Label for="sort" sm={2} size="sm">Sort:</Label>
                <Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">First Name</Button>   
                <Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">Last Name</Button>  
                <Button onClick={props.onSubmit} size="lg" style={{ margin: '0px 5px'}} color="secondary">ID</Button>  
            </Col>


        </FormGroup>
      </Form>
    )
}
export default MyForm;

List component

import React, {Component, Fragment} from 'react';
import { Col, Form, FormGroup, Label, Input, Button } from 'reactstrap';

const List = (props) => {
    return(
        <Fragment>
        {props.employees.map( (item, i) => (
            <div style={{ margin: '40px 0px'}} key={i}>
                <hr style={{ border:'1px dashed #000'}}></hr>
                <div className="float-right">
                    <Button onClick={props.onEdit}  size="lg" style={{ margin: '0px 5px'}} color="secondary">{props.Edit ? 'Save': 'Edit'}</Button> 
                    <Button  size="lg"  style={{ margin: '0px 5px'}}color="secondary">Delete</Button>
                </div>
                <FormGroup row>
                    <Col sm={5}>

                        {props.Edit ? (
                             <Input type="text" onChange={props.onChange} value={ props.updatedFirst ? props.updatedFirst : item.firstName } name="updatedFirst" placeholder="Enter First Name"/>
                        ):(
                           <div>
                          {props.updatedFirst ? props.updatedFirst : item.firstName } 
                           </div>
                        )}

                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={5}>

                        {props.Edit ? (
                             <Input type="text" onChange={props.onChange} value={ props.updatedEmployeeID ? props.updatedEmployeeID : item.employeeID} name="updatedEmployeeID" placeholder="Enter EmployeeID"/>
                        ):(
                           <div>
                          {props.updatedEmployeeID ? props.updatedEmployeeID : item.employeeID} 
                           </div>
                        )}

                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={5}>
                    {props.Edit ? (
                             <Input type="text" onChange={props.onChange} value={ props.updatedLast ? props.updatedLast: item.lastName} name="updatedLast" placeholder="Enter Last Name"/>
                        ):(
                           <div>
                          {props.updatedLast ? props.updatedLast : item.lastName} 
                           </div>
                    )}
                    </Col>
                </FormGroup>
            </div>
        ))}
    </Fragment>
    )
}

export default List;
norbitrial
  • 14,716
  • 7
  • 32
  • 59
BARNOWL
  • 3,326
  • 10
  • 44
  • 80
  • 1
    Pass the index onedit and onchange, in the handler use Array.prototype.map to set only the value of the object with that particular index. – HMR Sep 16 '19 at 21:09
  • would you able to provide a code example answer given your solution. – BARNOWL Sep 16 '19 at 21:24

2 Answers2

2

The following example shows how to pass a handler and set the state accordingly.

For good measure I separated the logic and the presentation, the presentational components are pure components using React.memo.

//A container should only contain the logic
class EmployeesContainer extends React.Component {
  state = {
    employees: [{ name: '' }, { name: '' }, { name: '' }],
  };
  //define what needs to happen if you click edit on an
  //  employee
  onEdit = index => {
    //edit will be called with the index of the employee
    //  the Employees component owns the list of employees
    //  so it will have to make changes to it
    this.setState({
      employees: this.state.employees.map((employee, i) =>
        i === index ? { ...employee, edit: true } : employee
      ),
    });
  };
  //Same idea as onEdit, index needs to be passed to indicate
  //  what employee needs to be changed
  onChange = (index, e) => {
    this.setState({
      employees: this.state.employees.map((employee, i) =>
        i === index
          ? { ...employee, name: e.target.value }
          : employee
      ),
    });
  };
  render() {
    return (
      <Employees
        employees={this.state.employees}
        onEdit={this.onEdit}
        onChange={this.onChange}
      />
    );
  }
}
//The Employees presentational component, contains the jsx
// you can make it a pure component by using React.memo
const Employees = React.memo(
  ({ employees, onEdit, onChange }) => (
    <div>
      {employees.map((employee, index) => (
        <EmployeeContainer
          key={index}
          index={index}
          employee={employee}
          onEdit={onEdit}
          onChange={onChange}
        />
      ))}
    </div>
  )
);
//Make this a container as well because it does more
//  than only produce jsx
class EmployeeContainer extends React.Component {
  state = {};
  //create onChange and onEdit only when index changes
  //  this will prevent unnecessary renders
  static getDerivedStateFromProps(props, state) {
    const { index, onChange, onEdit } = props;
    if (state.index !== index) {
      return {
        index,
        onChange: e => onChange(index, e),
        onEdit: () => onEdit(index),
      };
    }
    return null;
  }
  render() {
    const { employee } = this.props;
    const { onChange, onEdit } = this.state;
    return (
      <Employee
        employee={employee}
        onChange={onChange}
        onEdit={onEdit}
      />
    );
  }
}
//presentational component, is also pure component
const Employee = React.memo(
  ({ employee, onChange, onEdit }) => (
    <div>
      {employee.edit ? (
        <input
          type="text"
          value={employee.name}
          onChange={onChange}
        />
      ) : (
        <button onClick={onEdit}>edit</button>
      )}
    </div>
  )
);
HMR
  • 37,593
  • 24
  • 91
  • 160
-1

I don’t think onSubmit is updating employees correctly. You shouldn’t use this.state inside setState.

this.state inside setState ReactJS

Try this..

this.setState(prevState => ({
      employees: [...prevState.employees, ourForm]
  }, () => {
      console.log(this.state.employees)
  }))
Nathan Hall
  • 409
  • 2
  • 8
  • 17
  • still edits all input fields for each object. I don't think its the onSubmit handler, i think react needs to know what index to edit etc, im not sure how to implement this being that i have no id to compare it with. – BARNOWL Sep 16 '19 at 21:35
  • Can you use the employeeId for the index? – Nathan Hall Sep 16 '19 at 21:37
  • no because the employee id is not by increments like 0, 1, 2, 3, its a random 6 digit value. – BARNOWL Sep 16 '19 at 21:39
  • I think trying to have a single employee in state along with all employees is complicating everything. From what I can see, this should be broken up using single responsibility pattern. – Nathan Hall Sep 16 '19 at 21:50