Reputation: 165
I am trying to Close a modal from a child component of the modal component, my problem is, I have to declare a function on the grand parent component for the gradchild component and my code to update it isn't working. My test app uses react-bootstrap and currently has no store lib. What I am trying todo is nest a "form" component within a "modal" component and pass the closing function to the form component within the modal component all on a grand parent. the code is as follows:
This is the main component which contains my Modal Component and Form Component
import { useEffect } from "react";
import { GetAllGrades } from "../../../Services/GradeApi";
import FormModal from "../../Layout/Modal/FormModal";
import AddGradeModal from "./AddGradeModal";
import EditGradeModal from "./EditGradeModal";
import GradeForm from "./GradeForm";
const AllGrades = () => {
const [grades, setGrades] = GetAllGrades();
useEffect(() => {
setGrades();
}, [grades, setGrades]);
return (
<table className="table .table-striped ">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">KYU</th>
<th scope="col">Required Session Time</th>
<th>
</th>
<th></th>
</tr>
</thead>
<tbody>
{grades?.map((grade) => (
<tr key={grade.id}>
<th>{grade.name}</th>
<td>{grade.kyu}</td>
<td>{grade.requiredLessionTime}</td>
<td>
</td>
<td>
<FormModal ModalTitle="Form Modal" TriggerButtonText="Test">
<GradeForm Grade={grade} ViewOnly={false}>
closeModal={????} </GradeForm> //not sure how not to set this property for the callback function here
</FormModal>
</td>
</tr>
))}
</tbody>
</table>
);
};
export default AllGrades;
Modal Component - I am trying to add its "handlClose" function to the child component so it can call and close the modal once the for is submitted.
import React, { useEffect, useState } from "react";
import { Button, Modal } from "react-bootstrap";
type FormModalProps = {
ModalTitle: string;
TriggerButtonText: string;
children?: React.ReactNode;
};
const FormModal = (props: FormModalProps) => {
const { ModalTitle, TriggerButtonText, children } = props;
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const childrenWithProps = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, {
closeModal: { handleClose },
});
}
return child;
}); // update the children with the correct function
return (
<>
<Button variant="primary" onClick={handleShow}>
{TriggerButtonText}
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>{ModalTitle}</Modal.Title>
</Modal.Header>
<Modal.Body>{childrenWithProps}</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
};
export default FormModal;
this is the form - on the submit function I want to close the modal, so I pass the "handleClose" function from the parent.
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { Grade, GradeFormProps } from "../../../Interface";
import { useEffect, useState } from "react";
import { Button, Form } from "react-bootstrap";
import { GradeValidationSchema } from "../../../Services/ValidationSchemas/GradeValidation";
const GradeForm = (props: GradeFormProps) => {
const [grade, setGrade] = useState<Grade>(props.Grade);
const { Grade, ViewOnly, closeModal } = props;
const {
handleSubmit,
control,
formState: { errors },
} = useForm<Grade>({
resolver: yupResolver(GradeValidationSchema),
defaultValues: grade,
});
const onSubmit = (data: Grade) => {
if (data.id === undefined) {
// AddMember({ Grade: data });
} else {
// UpdateMember({ Grade: data });
}
console.log("close Form");
closeModal && closeModal(null);
// closeModal.setShow(true);
};
useEffect(() => {
setGrade(Grade);
}, [setGrade, grade, Grade]);
return (
<Form onSubmit={handleSubmit(onSubmit)}>
<fieldset disabled={ViewOnly}>
<Form.Group className="mb-3" controlId="name">
<Form.Label>Licence Number</Form.Label>
<Controller
control={control}
name="name"
defaultValue=""
render={({ field: { onChange, onBlur, value, ref } }) => (
<Form.Control
onChange={onChange}
value={value}
ref={ref}
placeholder="Enter Name"
/>
)}
/>
{errors.name && (
<div className="text-danger">{errors.name?.message}</div>
)}
</Form.Group>
<Form.Group className="mb-3" controlId="kyu">
<Form.Label>KYU</Form.Label>
<Controller
control={control}
name="kyu"
render={({ field: { onChange, onBlur, value, ref } }) => (
<Form.Control
onChange={onChange}
value={value}
ref={ref}
placeholder="Enter Kyu"
/>
)}
/>
{errors.kyu && (
<div className="text-danger">{errors.kyu?.message}</div>
)}
</Form.Group>
<Form.Group className="mb-3" controlId="requiredLessionTime">
<Form.Label>Last Name</Form.Label>
<Controller
control={control}
name="requiredLessionTime"
render={({ field: { onChange, onBlur, value, ref } }) => (
<Form.Control
onChange={onChange}
value={value}
ref={ref}
placeholder="Enter required Session Time"
/>
)}
/>
{errors.requiredLessionTime && (
<div className="text-danger">
{errors.requiredLessionTime?.message}
</div>
)}
</Form.Group>
{!props.ViewOnly && (
<Button variant="primary" type="submit">
Submit
</Button>
)}
</fieldset>
</Form>
);
};
export default GradeForm;
Props for the Form
export interface GradeFormProps{
Grade: Grade,
ViewOnly: boolean,
closeModal: (params:any)=>any ;
}
any help would be appreciated.
Upvotes: 0
Views: 498
Reputation: 76
I think, you just have a syntax issue in your FormModal
component. handleClose
shouldn't be wrapped in an object.
import React, { useEffect, useState } from "react";
import { Button, Modal } from "react-bootstrap";
type FormModalProps = {
ModalTitle: string;
TriggerButtonText: string;
children?: React.ReactNode;
};
const FormModal = (props: FormModalProps) => {
const { ModalTitle, TriggerButtonText, children } = props;
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const childrenWithProps = React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return React.cloneElement(child, {
closeModal: handleClose
});
}
return child;
}); // update the children with the correct function
return (
<>
<Button variant="primary" onClick={handleShow}>
{TriggerButtonText}
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>{ModalTitle}</Modal.Title>
</Modal.Header>
<Modal.Body>{childrenWithProps}</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
};
export default FormModal;
In the AllGrades
component, you don't need to have closeModal
in the JSX but this could be an old code fragment.
Additionally, I would recommend to rename the closeModal
prop of GradeForm
into something like onSuccess
or onFinish
, as the form doesn't know that it is rendered within a modal or not.
Upvotes: 1