从道具值设置状态以发出更新请求

Setting State From props Values To Make An UPDATE Request

我有项目对象:

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

我有一个组件,当用户单击“更新项目”按钮时会调用该组件。他们点击的特定 Project 通过 props 传递到 UpdateProject 组件。

当我尝试 console.log(props.project)const updateProject = (props) => { 行的正下方时,我可以看到对象进来了。太棒了。 :-)

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

但是,我无法在此组件中设置状态。我需要能够更新 Project 对象的详细信息并将该更新请求发送到服务器。 非常感谢任何帮助。

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);
  };

对于组件的其余部分,我在 return() 方法中的输入如下所示,例如:

        <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>

anti-pattern 将传递的 props 存储到本地组件状态,直接使用 prop 值即可。您还在提交处理程序中错误地引用了 thisthis OFC 只是在函数组件中未定义。

据我所知,您想要从 props 初始化 projectName,在表单中更新此值,并在提交表单后,将此 projectName 状态与项目对象一起使用作为 props 传递以更新项目。

使用 useEffect 挂钩使本地状态与 project 对象保持同步 if/when props.project 更新。

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

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

将此 projectName 状态与提交处理程序中的 props.project 对象合并。

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);
};

表单应使用受控输入,使用 projectName 状态作为值。

<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>

更新

由于您正试图将 class 组件转换为函数组件,因此这里有一些基本步骤:

  1. 将class转换为函数和主体,将render方法更改为函数return。
  2. 将组件 state 转换为 useState 挂钩。
  3. componentDidMountcomponentDidUpdatecomponentWillUnmount 生命周期方法转换为一个或多个具有适当依赖项数组的 useEffect 挂钩。
  4. 将所有对 this.state 的引用转换为新的状态变量,并将 this.props 转换为 props 或从 props.
  5. 解构的任何变量

Class component being converted

函数组件版本:

const UpdateProject = ({ createProject, getProject, project }) => {
  const navigate = useNavigate();
  const { id } = useParams();

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

  useEffect(() => {
    setState(project); // update project state when project prop updates
  }, [project]);

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

  const onChange = (e) => {
    setState((state) => ({
      ...state,
      [e.target.name]: e.target.value
    }));
  };

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

    const updateProject = {
      ...state
    };

    createProject(updateProject, navigate);
  };

  return (
    <div className="project">
      <div className="container">
        <div className="row">
          <div className="col-md-8 m-auto">
            <h5 className="display-4 text-center">Update Project form</h5>
            <hr />
            <form onSubmit={onSubmit}>
              <div className="form-group">
                <input
                  type="text"
                  className="form-control form-control-lg "
                  placeholder="Project Name"
                  name="projectName"
                  value={state.projectName}
                  onChange={onChange}
                />
              </div>
              <div className="form-group">
                <input
                  type="text"
                  className="form-control form-control-lg"
                  placeholder="Unique Project ID"
                  name="projectIdentifier"
                  value={state.projectIdentifier}
                  onChange={onChange}
                  disabled
                />
              </div>
              <div className="form-group">
                <textarea
                  className="form-control form-control-lg"
                  placeholder="Project Description"
                  name="description"
                  onChange={onChange}
                  value={state.description}
                />
              </div>
              <h6>Start Date</h6>
              <div className="form-group">
                <input
                  type="date"
                  className="form-control form-control-lg"
                  name="startDate"
                  value={state.startDate}
                  onChange={onChange}
                />
              </div>
              <h6>Estimated End Date</h6>
              <div className="form-group">
                <input
                  type="date"
                  className="form-control form-control-lg"
                  name="endDate"
                  value={state.endDate}
                  onChange={onChange}
                />
              </div>

              <input type="submit" className="btn btn-primary btn-block mt-4" />
            </form>
          </div>
        </div>
      </div>
    </div>
  );
};

添加 Redux

虽然您可以仍然使用react-redux中的connect高阶组件,但现在更常见的是使用useDispatchuseSelector 挂钩.

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));
  };

  ...