简体   繁体   English

无法将表单输入值从 function 传递给反应中的组件

[英]Can't pass form input values from function to component in react

I have a multi-step form using kendo and basically what i want to do is to pass the values from the function Charging to one of the steps so i can use the user inputs in a fetch API GET request.我有一个使用剑道的多步骤表单,基本上我想做的是将 function 充电中的值传递到其中一个步骤,以便我可以在获取 API GET 请求中使用用户输入。 Normally i would have done it with props, but the thing here is that i am passing data from a function.通常我会用道具来做,但这里的事情是我从 function 传递数据。 I tried useContext but i can't make it work and i went through the kendo form code many times in order to grasp its methodology but i still can't pass the values.我尝试了useContext,但我无法让它工作,我多次浏览剑道表单代码以掌握它的方法,但我仍然无法传递值。 I am getting the values from onStepSubmit() handler and i can get them outside this callback with useState, but even then i can't pass them.我从 onStepSubmit() 处理程序中获取值,我可以使用 useState 在此回调之外获取它们,但即使那样我也无法传递它们。

Here is the code for the main function where i get the values这是主要 function 的代码,我在其中获取值

import * as React from "react";
import "./Main.css";
import { Form, FormElement } from "@progress/kendo-react-form";
import { Button } from "@progress/kendo-react-buttons";
import { Stepper } from "@progress/kendo-react-layout";
import { SelectVehicle } from "./components/SelectVehicle";
import { PaymentMethod } from "./components/PaymentMethod";
import chargeIcon from "../../../../img/svg-6.svg";
import { ChargingStep } from "./components/ChargingStep";
import { Payment } from "./components/Payment";
import axios from "axios";
import { AuthContext } from "../../../../shared/context/auth-context";
import Notification from "../../Vehicles/Vehicles1/components/Notification";

const stepPages = [SelectVehicle, PaymentMethod, ChargingStep, Payment];

export const Charging = () => {
  const [step, setStep] = React.useState(0);
  const [formState, setFormState] = React.useState({});
  const [steps, setSteps] = React.useState([
    { label: "Select Vehicle", isValid: undefined },
    { label: "Method", isValid: undefined },
    { label: "Charging", isValid: undefined },
    { label: "Payment", isValid: undefined },
  ]);
  const auth = React.useContext(AuthContext);
  const [vehicleId, setVehicleId] = React.useState(false);
  const [notify, setNotify] = React.useState({
    isOpen: false,
    message: "",
    type: "",
  });

  const lastStepIndex = steps.length - 1;
  const isLastStep = lastStepIndex === step;
  const isPreviousStepsValid =
    steps
      .slice(0, step)
      .findIndex((currentStep) => currentStep.isValid === false) === -1;

  const onStepSubmit = React.useCallback(
    //add fetch vehicle data based on ID
    (event) => {
      const { isValid, values } = event;
      axios
        .get(process.env.REACT_APP_BACKEND_URL + `/cars/user/${auth.userId}`)
        .then((response) => {
          for (var i = 0; i < response.data.vehicles.length; i++) {
            if (values.vehicleID == response.data.vehicles[i]._id) {
              setVehicleId(true);
              return;
            } else {
              setVehicleId(false);
              return;
            }
          }
        });

      const currentSteps = steps.map((currentStep, index) => ({
        ...currentStep,
        isValid: index === step ? isValid : currentStep.isValid,
      }));

      setSteps(currentSteps);
      setStep(() => Math.min(step + 1, lastStepIndex));
      setFormState(values);

      if (isLastStep && isPreviousStepsValid && isValid && vehicleId) {
        // Send to api the data
        //alert(JSON.stringify(values));
        setNotify({
          isOpen: true,
          message: "Submitted Successfully",
          type: "success",
        });
      } else if (isLastStep && isPreviousStepsValid && isValid && !vehicleId) {
        setNotify({
          isOpen: true,
          message: "Wrong vehicle ID input",
          type: "error",
        });
      }
    },
    [
      step,
      steps,
      setSteps,
      setStep,
      setFormState,
      lastStepIndex,
      isLastStep,
      isPreviousStepsValid,
    ]
  );
  const onPrevClick = React.useCallback(
    (event) => {
      event.preventDefault();
      setStep(() => Math.max(step - 1, 0));
    },
    [step, setStep]
  );

  return (
      <div>
        <div className="vehicle__title">
          <div className="main__title">
            <img src={chargeIcon} alt="charging" />
            <div className="main__greeting">
              <h1>Charging Simulator</h1>
              <p>Simulate a Charge</p>
            </div>
          </div>
        </div>
        <div className="wrapper__simulator">
          <div
            style={{
              display: "flex",
              flexDirection: "column",
              justifyContent: "center",
            }}
          >
            <Stepper value={step} items={steps} />
            <Form
              initialValues={formState}
              onSubmitClick={onStepSubmit}
              render={(formRenderProps) => (
                <div style={{ alignSelf: "center" }}>
                  <FormElement style={{ width: 480 }}>
                    {stepPages[step]}
                    <span
                      style={{ marginTop: "40px" }}
                      className={"k-form-separator"}
                    />
                    <div
                      style={{
                        justifyContent: "space-between",
                        alignContent: "center",
                      }}
                      className={"k-form-buttons k-buttons-end"}
                    >
                      <span style={{ alignSelf: "center" }}>
                        Step {step + 1} of 4
                      </span>
                      <div>
                        {step !== 0 ? (
                          <Button
                            style={{ marginRight: "16px" }}
                            onClick={onPrevClick}
                          >
                            Previous
                          </Button>
                        ) : undefined}
                        <Button
                          primary={true}
                          disabled={
                            isLastStep
                              ? !isPreviousStepsValid && !vehicleId
                              : false
                          }
                          onClick={formRenderProps.onSubmit}
                        >
                          {isLastStep ? "Submit" : "Next"}
                        </Button>
                      </div>
                    </div>
                  </FormElement>
                </div>
              )}
            />
          </div>
        </div>
        <Notification notify={notify} setNotify={setNotify} />
      </div>
  );
};

export default Charging;

And here is the code for the component where i need these values.这是我需要这些值的组件的代码。 In the async componentDidMount function i want the url to be http://localhost:8765/evcharge/api/providers/${values.stationID}/${values.pointID} and get the params. In the async componentDidMount function i want the url to be http://localhost:8765/evcharge/api/providers/${values.stationID}/${values.pointID} and get the params.

class OneStep extends React.Component {  

data = [
    { text: "100%", id: 1 },
    { text: "75%", id: 2 },
    { text: "50%", id: 3 },
  ];
  state = {
    value: { text: "100%", id: 1 },
    cost: {text: "", id: null}
  };
  providers = [];
  

  async componentDidMount() {
    const url = "http://localhost:8765/evcharge/api/providers";
    const response = await fetch(url);
    const data = await response.json();
    for (var i = 0; i < data.providers.length; i++) {
      this.providers.push({
        text: "Provider: " +
          data.providers[i]
            .Title + " Cost: " + data.providers[i].kWhCost,
        id: i + 1 ,
      });
    }
  }

  numberFrom = getRandomInt(30, 50, 0);
  cost = getRandomInt(0.5, 2, 2);
  handleChange = (event) => {
    this.setState({
      value: event.target.value,
    });
    console.log(this.data);
    console.log(this.providers);
  };

  handleSecondChange = (event) => {
    this.setState({
      cost: event.target.value
    })
  }
  render() {
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between" }}>
          <div style={{ width: "50%", marginRight: "25px" }}>
            <Button
              style={{
                width: "50%",
                marginRight: "25px",
                marginTop: "35px",
                textTransform: "capitalize",
                color: "#0779e4",
                fontWeight: "600",
                fontSize: "18px",
                right: "50px",
              }}
              disabled={true}
              look="flat"
            >
              From: {this.numberFrom}
            </Button>
          </div>
          <Button
            style={{
              width: "50%",
              marginRight: "25px",
              marginTop: "35px",
              textTransform: "capitalize",
              color: "#0779e4",
              fontWeight: "600",
              fontSize: "18px",
            }}
            disabled={true}
            look="flat"
          >
            Cost per kWh: {this.cost}
          </Button>
        </div>
        <br />
        <div style={{ display: "flex", justifyContent: "space-between" }}>
          <div style={{ width: "25%", marginRight: "25px" }}>
            <DropDownList
              data={this.data}
              dataItemKey="id"
              value={this.state.value}
              onChange={this.handleChange}
              textField="text"
              defaultItem={{ text: "To" }}
            />
          </div>
          <div style={{ width: "75%", marginRight: "25px" }}>
            <DropDownList
              data={this.providers}
              dataItemKey="id"
              value={this.state.providers}
              onChange={this.handleSecondChange}
              textField="text"
              defaultItem={{ text: "Select Provider..." }}
            />
          </div>
        </div>
        <br />
        <div
          style={{
            display: "flex",
            justifyContent: "space-between",
            height: "250px",
          }}
        >
          <div style={{ width: "50%", marginLeft: "15px" }}>
            <Button
              style={{
                width: "50%",
                marginRight: "25px",
                marginTop: "35px",
                textTransform: "capitalize",
                color: "#ff5349",
                fontWeight: "600",
                fontSize: "18px",
                right: "30px",
              }}
              disabled={true}
              look="flat"
            >
              <CountUp
                start={0}
                end={parseInt(
                  (parseFloat(this.state.value.text) - this.numberFrom) *
                    this.cost
                )}
                duration={15}
                useEasing={true}
                decimals={2}
                prefix="Expected Cost:  "
                suffix=" €"
                useGrouping={true}
                delay={3}
              />
            </Button>
          </div>
          <div
            style={{ width: "50%", marginRight: "25px", marginBottom: "450px" }}
          >
            <div className="g-container">
              <div className="g-number">
                <CountUp
                  start={30}
                  end={parseInt(this.state.value.text)}
                  duration={15}
                  useEasing={true}
                  decimals={2}
                  suffix=" %"
                  useGrouping={true}
                  delay={3}
                />
              </div>
              <div className="g-contrast">
                <div className="g-circle"></div>
                <ul className="g-bubbles">
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                  <li></li>
                </ul>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

export const ChargingStep = <OneStep />;

Instead of exporting and using the rendered output of the OneStep component, just export and use the OneStep component itself, then you can pass whatever props you need to it.无需导出和使用OneStep组件的渲染 output ,只需导出并使用OneStep组件本身,然后您可以将所需的任何道具传递给它。

Note there's no issue here with having a functional and class-based component as from the outside they're identical.请注意,这里有一个功能性和基于类的组件没有问题,因为从外部来看它们是相同的。

Start with the file that contains OneStep , change the export statement from export const ChargingStep = <OneStep />;从包含OneStep的文件开始,将export语句从export const ChargingStep = <OneStep />; to export const ChargingStep = OneStep; export const ChargingStep = OneStep; (or ideally just rename OneStep to ChargingStep and export it directly). (或者理想情况下只是将OneStep重命名为ChargingStep并直接导出)。 Note you'll also have to do this with the other step components so they all work the same (but this is how React components should be exported and used anyway).请注意,您还必须对其他 step 组件执行此操作,以便它们都工作相同(但这就是 React 组件应该导出和使用的方式)。

Then in the Charging component you can change the line in the return statement from {stepPages[step]} to something like:然后在Charging组件中,您可以将 return 语句中的行从{stepPages[step]}更改为:

const StepPage = stepPages[step];

return (
    // ...
    <StepPage relevantProp={value}/>
    // ...
)

Or you can add special handling for just the ChargingStep step if you don't want to pass those same components to all the other steps, which I'd recommend here.或者,如果您不想将这些相同的组件传递给所有其他步骤,您可以仅为ChargingStep步骤添加特殊处理,我在此推荐。

Further Refactoring进一步重构

You might consider slightly changing the way you keep track of what step the user is on from a straight index lookup to using string names so you can tell which component is going to be rendered.您可能会考虑稍微改变跟踪用户执行步骤的方式,从直接索引查找改为使用字符串名称,这样您就可以知道要呈现哪个组件。

You could do something like this:你可以这样做:

const stepPages = {
    "SelectVehicle": SelectVehicle,
    "PaymentMethod": PaymentMethod,
    "ChargingStep": ChargingStep,
    "Payment": Payment,
};

const stepPageNames = ["SelectVehicle", "PaymentMethod", "ChargingStep", "Payment"];

Then to get the step you're on:然后得到你正在采取的步骤:

const stepPageName = stepPageNames[step];
const StepPage = stepPages[stepPageName];

Then you can do things like:然后您可以执行以下操作:

let stepPage = <StepPage />
if (stepPageName === "ChargingStep") {
    stepPage = <StepPage relevantProp={value}/>
}

And place stepPage in your return statement.并将stepPage放在您的 return 语句中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM