我在多个组件上重复useReducer,它返回具有不同道具的相同组件

I am repeating useReducer at multiple components which returns the same component with different props

提问人:Amr Ahmad 提问时间:7/18/2023 最后编辑:marc_sAmr Ahmad 更新时间:7/18/2023 访问量:19

问:

我使用 React 创建了一个具有多个表单的 Web 应用程序。因此,我决定创建一个自定义表单,其中包含我在表单中使用的所有输入字段。

表单获取 prop 输入,其中包含要渲染和 prop 的所有字段。onSubmit

我不知道这是否是一个好主意。

我用来管理有关表单提交的多种状态,如果它成功,加载或错误。useReducer

问题是我在每个表单上重复,并且无法在自定义表单中使用它,因为我正在属于表单的函数上调度操作。useReduceronSubmit

下面是自定义表单组件:

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 函数及其状态。有没有更好的解决方案?

reactjs react-hooks

评论


答: 暂无答案