提问人:Amr Ahmad 提问时间:7/18/2023 最后编辑:marc_sAmr Ahmad 更新时间:7/18/2023 访问量:19
我在多个组件上重复useReducer,它返回具有不同道具的相同组件
I am repeating useReducer at multiple components which returns the same component with different props
问:
我使用 React 创建了一个具有多个表单的 Web 应用程序。因此,我决定创建一个自定义表单,其中包含我在表单中使用的所有输入字段。
表单获取 prop 输入,其中包含要渲染和 prop 的所有字段。onSubmit
我不知道这是否是一个好主意。
我用来管理有关表单提交的多种状态,如果它成功,加载或错误。useReducer
问题是我在每个表单上重复,并且无法在自定义表单中使用它,因为我正在属于表单的函数上调度操作。useReducer
onSubmit
下面是自定义表单组件:
import React, { useContext, useEffect } from "react";
import { FirebaseContext } from "context/FirebaseContext";
import { useForm } from "react-hook-form";
import Form from "react-bootstrap/Form";
import Alert from "react-bootstrap/Alert";
import Button from "react-bootstrap/Button";
import Toast from "react-bootstrap/Toast";
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
export default function CustomForm({ label, state, onSubmit, inputs }) {
const { servicesData, clientsData } = useContext(FirebaseContext);
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
useEffect(() => {
window.scrollTo(0, 0);
}, []);
return (
<>
<Alert variant='primary m-4'>
<h2>{label}</h2>
</Alert>
<Container className='w-50 mx-auto mt-4 form-container'>
<form onSubmit={handleSubmit(onSubmit)}>
{inputs?.find((i) => i === "newClientName") && (
<>
<Form.Group className='mb-3'>
<Form.Label>
Name <span>*</span>
</Form.Label>
<Form.Control
{...register("newClientName", {
required: true,
pattern: /^[A-Za-z ]+$/i,
})}
/>
{errors.newClientName?.type === "required" && (
<span className='error'>
{" "}
This field is required.
</span>
)}
{errors.newClientName?.type === "pattern" && (
<span className='error'>
{" "}
Name must contain letters only.
</span>
)}
</Form.Group>
</>
)}
{inputs?.find((i) => i === "selectClient") && (
<>
<Form.Group className='mb-3'>
<Form.Label>
Name <span>*</span>
</Form.Label>
<Form.Select
{...register("clientName", {
required: true,
})}
>
{clientsData.loading && (
<option disabled selected>
Loading ...
</option>
)}
{clientsData?.clients?.map((c) => (
<option key={c.name} value={c.name}>
{c.name}
</option>
))}
</Form.Select>
{errors.clientName?.type === "required" && (
<span className='error'>
{" "}
This field is required
</span>
)}
</Form.Group>
</>
)}
{inputs?.find((i) => i === "newClient") && (
<>
<Row>
<Col>
<Form.Group className='mb-3'>
<Form.Label>
Code <span>*</span>
</Form.Label>
<Form.Control
type='number'
{...register("code", {
required: true,
})}
/>
{errors.code?.type === "required" && (
<span className='error'>
{" "}
This field is required.
</span>
)}
</Form.Group>
</Col>
<Col>
<Form.Group className='mb-3'>
<Form.Label>
Registration Number
</Form.Label>
<Form.Control
type='number'
{...register("reg", {})}
/>
</Form.Group>
</Col>
</Row>
</>
)}
{inputs?.find((i) => i === "service") && (
<Form.Group className='mb-3'>
<Form.Label>
Service <span>*</span>
</Form.Label>
<Form.Select
{...register("service", { required: true })}
>
{servicesData.loading && (
<option disabled selected>
Loading ...
</option>
)}
{servicesData.services?.map((s) => (
<option key={s.name} value={s.name}>
{s.name}
</option>
))}
</Form.Select>
{errors.service && (
<span className='error'>
{" "}
This field is required
</span>
)}
</Form.Group>
)}
{inputs?.find((i) => i === "cost") && (
<Form.Group className='mb-3'>
<Form.Label>
Cost <span>*</span>
</Form.Label>
<Form.Control
type='number'
{...register("cost", { required: true })}
/>
{errors.cost && (
<span className='error'>
{" "}
This field is required
</span>
)}
</Form.Group>
)}
{inputs?.find((i) => i === "payment") && (
<Form.Group className='mb-3'>
<Form.Label>
Payment <span>*</span>
</Form.Label>
<Form.Control
type='number'
{...register("payment", { required: true })}
/>
{errors.payment && (
<span className='error'>
{" "}
This field is required
</span>
)}
</Form.Group>
)}
{inputs?.find((i) => i === "expense") && (
<>
<Form.Group className='mb-3'>
<Form.Label>
Cost <span>*</span>
</Form.Label>
<Form.Control
type='number'
{...register("expcost", {
required: true,
})}
/>
{errors.expcost && (
<span className='error'>
{" "}
This field is required
</span>
)}
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>
Expense <span>*</span>
</Form.Label>
<Form.Control
{...register("expense", {
required: true,
})}
/>
{errors.expense && (
<span className='error'>
{" "}
This field is required
</span>
)}
</Form.Group>
</>
)}
{inputs?.find((i) => i === "dateAndComment") && (
<>
<Form.Group className='mb-3'>
<Form.Label>
Date <span>*</span>
</Form.Label>
<Form.Control
type='date'
{...register("date", { required: true })}
/>
{errors.date && (
<span className='error'>
{" "}
This field is required
</span>
)}
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>Comment</Form.Label>
<Form.Control
as='textarea'
{...register("comment", {})}
/>
</Form.Group>
</>
)}
{inputs?.find((i) => i === "newClient") && (
<>
<Form.Group className='mb-3'>
<Form.Label>Address</Form.Label>
<Form.Control {...register("address", {})} />
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>Phone</Form.Label>
<Form.Control {...register("phone", {})} />
{errors.date?.type === "pattern" && (
<span className='error'>
{" "}
Phone Number must not have letters
</span>
)}
</Form.Group>
</>
)}
{state?.success && (
<Toast bg='success' autohide='true'>
<Toast.Body>
<strong>Saved.</strong>
</Toast.Body>
</Toast>
)}
{state?.error && (
<Toast bg='warning' autohide='true'>
<Toast.Body>
<strong>{state?.error}</strong>
</Toast.Body>
</Toast>
)}
{state?.loading ? (
<Button variant='primary w-100 mt-3' disabled>
Saving…
</Button>
) : (
<Button
variant='outline-primary w-100 mt-3'
type='submit'
>
Submit
</Button>
)}
</form>
</Container>
</>
);
}
这是我使用的表格之一:
import React, { useReducer, useContext } from "react";
import { setDoc, doc, arrayUnion } from "firebase/firestore";
import { FirebaseContext } from "context/FirebaseContext";
import { db } from "firebase-config";
import CustomForm from "components/CustomForm";
const reducer = (state, action) => {
switch (action.type) {
case "SUCCESS":
return { success: true, loading: false, error: "" };
case "LOADING":
return { success: false, loading: true, error: "" };
case "ERROR":
return { success: false, loading: false, error: action.payload };
}
};
export default function ClientForm() {
const [state, dispatch] = useReducer(reducer, {
success: false,
loading: false,
error: "",
});
const { clientsData } = useContext(FirebaseContext);
const inputs = [
"newClientName",
"newClient",
"service",
"cost",
"payment",
"dateAndComment",
];
const onSubmit = async (data) => {
try {
dispatch({ type: "LOADING" });
if (clientsData.error) {
dispatch({
type: "ERROR",
payload: "Can't fetch clients data.",
});
return;
}
if (!clientsData.loading) {
if (
clientsData.clients.find(
(c) => c.name === data.newClientName
)
) {
dispatch({
type: "ERROR",
payload: "Name already exists.",
});
return;
}
if (clientsData.clients.find((c) => c.code === data.code)) {
dispatch({
type: "ERROR",
payload: "Code already exists.",
});
return;
}
if (clientsData.clients.find((c) => c.reg === data.reg)) {
dispatch({
type: "ERROR",
payload: "Registeration number already exists.",
});
return;
}
} else {
dispatch({
type: "ERROR",
payload: "Can't fetch clients data.",
});
return;
}
let newTransaction = {
service: data.service,
cost: data.cost,
payment: data.payment,
comment: data.comment,
date: `${new Date(data.date).getDate()}/${
new Date(data.date).getMonth() + 1
}/${new Date(data.date).getFullYear()}`,
};
let clientRef = await doc(db, "Clients", data.newClientName);
await setDoc(clientRef, {
transaction: arrayUnion(newTransaction),
name: data.newClientName,
code: data.code,
reg: data.reg,
address: data.address,
phone: data.phone,
});
dispatch({ type: "SUCCESS" });
} catch (e) {
dispatch({
type: "ERROR",
payload: setError(`Error adding new client: (${e.message})`),
});
}
};
return (
<CustomForm
label={"New Client"}
onSubmit={onSubmit}
state={state}
inputs={inputs}
/>
);
}
谢谢。
我认为我可以制作文件并导出 useReducer 函数及其状态。有没有更好的解决方案?
答: 暂无答案
下一个:避免多层比较函数中的重复
评论