0

In my react app, i have a Tables file and another for my popup Modal which is supposed to be activated by an Edit button found in each table row.

Whenever i load my app, the app throws in an error pointing to my Modal file

**TypeError: Cannot read property 'name' of undefined** 
 23 | <Label for="title">Name</Label>
  24 | <Input
  25 |   id="name"
> 26 |   value={props.editEmployeeData.name}
     | ^  27 |   onChange={(e) => {
  28 |     let { editEmployeeData } = props; // destructuring assignment
  29 |     editEmployeeData.name = e.target.value;

I have tried to find a fix but nothing works.

My Tables component looks like this

const EmployeeTable = () => {

  const [state, setState] = React.useState({
    employeesDetail: [],
    editEmployeeData: {
      id: "",
      name: "",
      phone_no: "",
      email: "",
      department: "",
      job_title: "",
      salary: "",
      date_employed: "",
    },
    editEmployeeModal: false,
  });

  const _refreshBooks = ()=>{
        axios
          .get("http://localhost:8000/api/accounts/employees_hr/")
          .then((response) =>
            setState({
              employeesDetail: response.data.results,
            })
          );
    }

  useEffect(() => {
    _refreshBooks();
  }, []);

  // modal appear/disappear func
  const toggleEditEmployeeModal = () => {
      setState({
        editEmployeeModal: !state.editEmployeeModal,
      });
  };

  // Populate Edit book func - 'Edit button'
  const editEmployeeData = (id, name, phone_no, email, department, job_title, salary, date_employed) =>{
      setState({
          editEmployeeData: {id, name, phone_no, email, department, job_title, salary, date_employed}, 
          editEmployeeModal: !state.editEmployeeModal
      })
  }

   // Edit func - 'Update Book button'
  const updateRecord=()=>{
        let {
          name,
          phone_no,
          email,
          department,
          job_title,
          salary,
          date_employed,
        } = state.editEmployeeData;

        axios
          .put(
            "http://localhost:8000/api/accounts/employees_hr/" +
              state.editEmployeeData.id,
            {
              name,
              phone_no,
              email,
              department,
              job_title,
              salary,
              date_employed,
            }
          )

          .then((response) => 
            _refreshBooks()
          );
        // close modal after edit and set the 'editEmployeeData' fields to empty
        setState({
          editBookModal: false,
          editEmployeeData: {
            id: "",
            name: "",
            phone_no: "",
            email: "",
            department: "",
            job_title: "",
            salary: "",
            date_employed: "",
          } //clear
        });
  }

  const classes = useStyles();

  const columns = [
    // "id",
    { name: "name", label: "Name" },
    { name: "phone_no", label: "Contact" },
    { name: "email", label: "Email" },
    { name: "department", label: "Department" },
    { name: "job_title", label: "Title" },
    { name: "salary", label: "Salary" },
    { name: "date_employed", label: "Date Employed" },
    {
      name: "Action",
      options: {
        filter: true,
        sort: false,
        empty: true,
        customBodyRender: (value, tableMeta, updateValue) => {
          const i = tableMeta.rowIndex;
          const data = state.employeesDetail;
          return (
            // open modal
            <Button
              color="success"
              size="sm"
              onClick={() =>
                editEmployeeData(
                  data[i].id,
                  data[i].name,
                  data[i].phone_no,
                  data[i].email,
                  data[i].department,
                  data[i].job_title,
                  data[i].salary,
                  data[i].date_employed,
                )
              }
            >
              Edit
            </Button>
          );
        },
      },
    },
  ];

  return (
    <div className={classes.root}>
      {/* EDIT BOOK MODAL */}
      <EditEmployeeModal
        editEmployeeModal={state.editEmployeeModal}
        editEmployeeData={state.editEmployeeData}
        updateRecord={updateRecord}  
        toggleEditEmployeeModal={toggleEditEmployeeModal}     
      />

As for my Modal file, the setup looks like this

const EditEmployeeModal = (props) => {

  return (
    <div className="App container">
      <Modal isOpen={props.editEmployeeModal}>
        <ModalHeader toggle={props.toggleEditEmployeeModal}>
          Update Employee Record
        </ModalHeader>
        <ModalBody>
          <FormGroup>
            <Label for="title">Name</Label>
            <Input
              id="name"
              value={props.editEmployeeData.name}
              onChange={(e) => {
                let { editEmployeeData } = props; // destructuring assignment
                editEmployeeData.name = e.target.value;
                props.setState({ editEmployeeData });
              }}
            />
          </FormGroup>

What i'm i missing or doing wrong based on the above code setup?

Shadow Walker
  • 979
  • 5
  • 27
  • 51
  • I don't see any kind of loading state or null check for while you're fetching data (apologies if there is one) but you might want to use a piece of state that you set to true when you initially begin the fetch for the data that's going to update your currently default state (and then false when the request is done). This way React will not try to render your component before it has the data. – kJs0 Jul 30 '20 at 12:29
  • I have ```useEffect(() => { _refreshBooks(); }, []);``` – Shadow Walker Jul 30 '20 at 12:30
  • Hey sorry - I mean something that toggles a loading state like a boolean of some kind that says "isLoading" right now your component is unaware that it needs to wait for you to finish fetching the data before it renders your component. – kJs0 Jul 30 '20 at 12:34
  • Oh, i don't have one, i suppose that why whenever the edit modal pops up, the records disappears meaning no reload happens to gets those records back in the table. Kindly advice on how to go about it. – Shadow Walker Jul 30 '20 at 12:41
  • You'll basically want to add a piece of state like `const [isLoading, setIsLoading] = React.useState(true);`. On mount you'll be in loading state now, you'll set this false after your network request is done in the same `.then` where you set the new state you just loaded. Then just check `if (loading) return null;` (you'll replace this null return with a loading spinner or some loading UI) before you return `EditEmployeeModal`. The null checking offered below in a solution is also viable but you are still left without your component knowing its fetching data so it depends on what you need. – kJs0 Jul 30 '20 at 12:41

2 Answers2

0

An easy way would be to check whether it is defined first, so:

UPDATE: You'll want to make this a controlled component, where the value prop represents the current value, and onChange is passed as a prop to update the value specified as props.editEmployeeData.name.

See this Answer on "What are React Controlled Components and Uncontrolled Components" for specifics

In your case, function passed to the onChange handler would looks something like this:

onChange = (e) => {
    const newValue = e.target.value // result of what is typed in the Input
    // ... update the state of the respective variable

}

Additionally, a nice way of doing that is with the new(ish) Optional Chaining syntax:

<Input
    id="name" value={ props?.editEmployeeData?.name } 
    onChange={ props.onChange }
// ...
/>
bilo-io
  • 597
  • 3
  • 17
  • Though your way helps get the modal to appear, i can't change or make entry in that particular modal field. Also, i'm getting an error ```TypeError: props.setState is not a function onChange={(e) => { let { editEmployeeData } = props; // destructuring assignment editEmployeeData.salary = e.target.value; props.setState({ editEmployeeData }); ``` – Shadow Walker Jul 30 '20 at 12:37
0

Another option is use destructuring have default values.

Another Issue is you are not passing the setState as prop to Modal

const EditEmployeeModal = (props) => {
  const { editEmployeeData: { name = "" } = {} } = props;

  return (
    <div className="App container">
      <Modal isOpen={props.editEmployeeModal}>
        <ModalHeader toggle={props.toggleEditEmployeeModal}>
          Update Employee Record
        </ModalHeader>
        <ModalBody>
          <FormGroup>
            <Label for="title">Name</Label>
            <Input
              id="name"
              value={name}
              onChange={(e) => {
                let { editEmployeeData } = props; // destructuring assignment
                editEmployeeData.name = e.target.value;
                props.setState({ editEmployeeData });
              }}
            />
          </FormGroup>
        </ModalBody>
      </Modal>
      </div>)
}
Siva K V
  • 10,561
  • 2
  • 16
  • 29