Home > OS >  Setting State From props Values To Make An UPDATE Request
Setting State From props Values To Make An UPDATE Request

Time:02-12

I have Project objects:

{
    id: "",
    projectName: "" ,
    projectIdentifier: "" ,
    description:"" ,
    startDate: Date ,
    endDate: Date ,
}

I have a component that gets called when a user clicks on an "Update Project" button. The specific Project that they clicked on gets passed into the UpdateProject component via props.

When I try to console.log(props.project) right below the const updateProject = (props) => { line, I can see the object coming in. Great. :-)

{
    id: 3,
    projectName: "Name" ,
    projectIdentifier: "1234" ,
    description:"Project description" ,
    startDate: "02-11-2022" ,
    endDate: "02-11-2022" ,
}

However, I am having trouble setting the state in this component. I need to be able to update the details of the Project object and send that UPDATE request to the server. Any help is greatly appreciated.

import React, { useState, useEffect, useRef } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { useNavigate, useParams } from "react-router";
import { getProject, createProject } from "../../actions/projectActions";

const UpdateProject = (props) => {
  console.log(props.project); //the Project object is coming in!!!
  let navigate = useNavigate();
  const { id } = useParams();

  const [projectName, setProjectName] = useState(props.project.projectName);
  console.log(props.project.projectName); <---- this is defined in console.log :-)
  console.log(projectName); <---- this is "undefined"

  const [projectIdentifier, setProjectIdentifier] = useState(
    props.project.projectIdentifier
  );
  const [description, setDescription] = useState(props.project.description);
  const [startDate, setStartDate] = useState(props.project.startDate);
  const [endDate, setEndDate] = useState(props.project.endDate);
  const [errors, setErrors] = useState(props.project.errors);

  const [state, setState] = useState({
    id,
    projectName: props.project.projectName,
    projectIdentifier: props.project.projectIdentifier,
    description: props.project.description,
    startDate: props.project.startDate,
    endDate: props.project.endDate,
  });


console.log("state before useEffect: ", state); // <--- here, id is the only thing console logging. Every other key is returning 'undefined'

useEffect(() => {
    if (props.errors) {
       setErrors(props.errors);
    }

    props.onGetProject(id, navigate);

    console.log("STATE: ", state); <--- again, id is the only thing console logging. Every other key is returning 'undefined'
  }, [props.errors]);


  const onSubmit = (e) => {
    e.preventDefault();
    const updateProject = {
      id: this.state.id,
      projectName: this.state.projectName,
      projectIdentifier: this.state.projectIdentifier,
      description: this.state.description,
      startDate: this.state.startDate,
      endDate: this.state.endDate,
      errors: {},
    };

    props.onCreateProject(updateProject, navigate);
  };

For the rest of the component, my inputs in the return() method look like this, for example:

        <form onSubmit={onSubmit}>
              <div className="form-group">
                <input
                  type="text"
                  className={classNames("form-control form-control-lg ", {
                    "is-invalid": errors.projectName,
                  })}
                  placeholder="Project Name"
                  name="projectName"
                  defaultValue={props.project.projectName}
                  onChange={(e) => setProjectName(e.target.value)}
                />
                 {errors.projectName && (
                  <div className="invalid-feedback">{errors.projectName}</div>
                )} 
              </div>

CodePudding user response:

It's anti-pattern to store passed props into local component state, just consume the prop value directly. You are also incorrectly referencing a this in the submit handler. this is OFC simply undefined in function components.

From what I can tell you are wanting to initialize the projectName from props, update this value in a form, and upon submitting the form, use this projectName state along with the project object passed as props to update a project.

Use an useEffect hook to keep the local state synchronized with the project object if/when the props.project updates.

const [projectName, setProjectName] = useState(props.project.projectName);

useEffect(() => {
  setProjectName(props.project.projectName);
}, [props.project.projectName]);

Merge this projectName state with the props.project object in the submit handler.

const onSubmit = (e) => {
  e.preventDefault();
  const updateProject = {
    ...props.project, // <-- shallow copy props.project object
    id,               // <-- id from params
    projectName,      // <-- projectName state
    errors: {},
  };

  props.onCreateProject(updateProject, navigate);
};

The form should use a controlled input, using the projectName state as the value.

<form onSubmit={onSubmit}>
  <div className="form-group">
    <input
      type="text"
      className={classNames(
        "form-control form-control-lg ",
        { "is-invalid": errors.projectName }
      )}
      placeholder="Project Name"
      name="projectName"
      value={projectName}
      onChange={(e) => setProjectName(e.target.value)}
    />
    {errors.projectName && (
      <div className="invalid-feedback">{errors.projectName}</div>
    )} 
  </div>
</form>

Update

Since you are attempting to convert a class component to a function component, here are some basic steps:

  1. Convert class to function and body, change render method to be the function return.
  2. Convert component state into a useState hook.
  3. Convert the componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods into one or more useEffect hooks with appropriate dependency array.
  4. Convert all references to this.state to the new state variables, and this.props to props or any variables destructured from props.

Edit setting-state-from-props-values-to-make-an-update-request

Adding Redux

While you could still use the connect Higher Order Component from react-redux it's more common now to use the useDispatch and useSelector hooks.

import { useDispatch, useSelector } from 'react-redux';

const UpdateProject = ({ createProject, getProject, project }) => {

  ...

  const dispatch = useDispatch();
  const project = useSelector(state => state.project.project);

  ...

  useEffect(() => {
    dispatch(getProject(id, navigate)); // fetch project when id updates
  }, [id]);

  ...

  const onSubmit = (e) => {
    e.preventDefault();

    const updateProject = {
      ...state
    };

    dispatch(createProject(updateProject, navigate));
  };

  ...
  • Related