无法将表单输入值从函数传递到反应中的组件
Can't pass form input values from function to component in react
我有一个使用 kendo 的多步骤表单,基本上我想做的是将函数 Charging 中的值传递给其中一个步骤,这样我就可以在提取中使用用户输入 API GET 请求。通常我会用道具来完成它,但这里的事情是我从一个函数传递数据。我尝试了 useContext 但无法使其正常工作,我多次检查 kendo 表单代码以掌握其方法,但我仍然无法传递值。我正在从 onStepSubmit() 处理程序获取值,我可以使用 useState 在此回调之外获取它们,但即便如此我也无法传递它们。
这是我获取值的主要函数的代码
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;
这是我需要这些值的组件的代码。在异步 componentDidMount 函数中,我希望 url 为 http://localhost:8765/evcharge/api/providers/${values.stationID}/${values.pointID}
并获取参数。
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
组件的渲染输出,只需导出并使用 OneStep
组件本身,然后你就可以传递任何你需要的道具给它。
请注意,具有功能性且基于 class 的组件没有问题,因为从外部看它们是相同的。
从包含 OneStep
的文件开始,将 export
语句从 export const ChargingStep = <OneStep />;
更改为 export const ChargingStep = OneStep;
(或者最好将 OneStep
重命名为 ChargingStep
并直接导出)。请注意,您还必须对其他步骤组件执行此操作,以便它们都以相同的方式工作(但 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 语句中。
我有一个使用 kendo 的多步骤表单,基本上我想做的是将函数 Charging 中的值传递给其中一个步骤,这样我就可以在提取中使用用户输入 API GET 请求。通常我会用道具来完成它,但这里的事情是我从一个函数传递数据。我尝试了 useContext 但无法使其正常工作,我多次检查 kendo 表单代码以掌握其方法,但我仍然无法传递值。我正在从 onStepSubmit() 处理程序获取值,我可以使用 useState 在此回调之外获取它们,但即便如此我也无法传递它们。
这是我获取值的主要函数的代码
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;
这是我需要这些值的组件的代码。在异步 componentDidMount 函数中,我希望 url 为 http://localhost:8765/evcharge/api/providers/${values.stationID}/${values.pointID}
并获取参数。
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
组件的渲染输出,只需导出并使用 OneStep
组件本身,然后你就可以传递任何你需要的道具给它。
请注意,具有功能性且基于 class 的组件没有问题,因为从外部看它们是相同的。
从包含 OneStep
的文件开始,将 export
语句从 export const ChargingStep = <OneStep />;
更改为 export const ChargingStep = OneStep;
(或者最好将 OneStep
重命名为 ChargingStep
并直接导出)。请注意,您还必须对其他步骤组件执行此操作,以便它们都以相同的方式工作(但 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 语句中。