簡體   English   中英

無法將表單輸入值從 function 傳遞給反應中的組件

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

我有一個使用劍道的多步驟表單,基本上我想做的是將 function 充電中的值傳遞到其中一個步驟,以便我可以在獲取 API GET 請求中使用用戶輸入。 通常我會用道具來做,但這里的事情是我從 function 傳遞數據。 我嘗試了useContext,但我無法讓它工作,我多次瀏覽劍道表單代碼以掌握它的方法,但我仍然無法傳遞值。 我從 onStepSubmit() 處理程序中獲取值,我可以使用 useState 在此回調之外獲取它們,但即使那樣我也無法傳遞它們。

這是主要 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;

這是我需要這些值的組件的代碼。 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 />;

無需導出和使用OneStep組件的渲染 output ,只需導出並使用OneStep組件本身,然后您可以將所需的任何道具傳遞給它。

請注意,這里有一個功能性和基於類的組件沒有問題,因為從外部來看它們是相同的。

從包含OneStep的文件開始,將export語句從export const ChargingStep = <OneStep />; export const ChargingStep = OneStep; (或者理想情況下只是將OneStep重命名為ChargingStep並直接導出)。 請注意,您還必須對其他 step 組件執行此操作,以便它們都工作相同(但這就是 React 組件應該導出和使用的方式)。

然后在Charging組件中,您可以將 return 語句中的行從{stepPages[step]}更改為:

const StepPage = stepPages[step];

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

或者,如果您不想將這些相同的組件傳遞給所有其他步驟,您可以僅為ChargingStep步驟添加特殊處理,我在此推薦。

進一步重構

您可能會考慮稍微改變跟蹤用戶執行步驟的方式,從直接索引查找改為使用字符串名稱,這樣您就可以知道要呈現哪個組件。

你可以這樣做:

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

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

然后得到你正在采取的步驟:

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

然后您可以執行以下操作:

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

並將stepPage放在您的 return 語句中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM