I have this simplified structure:
<Page>
<Modal>
<Form />
</Modal>
</Page>
All of these are functional components.
And in <Modal />
I have a close
function that looks like this:
const close = () => {
// apply a CSS class - so the modal disappears animatedly ..
// then use setTimeout() to completely remove the modal after the animation ends ..
}
Do you have an idea how the <Page />
component can call the <Modal />
close method? And the page has to do it because this is where I'm doing the call to API with the data from the form, and so if all is OK with API request - close the modal.
(The <Form />
handles only the form validation but then passes the data to <Page />
where all the business logic happens.)
PS: The project uses Typescript ... so I have to deal with types as well :(
CodePudding user response:
I look into your problem. I think my example should clarify your problem. Let me know if you have any questions.
import { ReactNode, useCallback, useState } from 'react'
import { render } from 'react-dom'
function App() {
return (
<div>
<Page />
</div>
)
}
function Page() {
const [isModalOpen, setModalOpen] = useState(false)
const handleFormSubmit = useCallback((formValues: FormValues) => {
console.log({ formValues })
setModalOpen(false)
}, [])
return (
<div>
<button onClick={() => setModalOpen(!isModalOpen)}>Toggle modal</button>
<Modal isOpen={isModalOpen}>
<Form onSubmit={handleFormSubmit} />
</Modal>
</div>
)
}
interface ModalProps {
isOpen: boolean
children: ReactNode
}
function Modal(props: ModalProps) {
if (!props.isOpen) return null
return <div className="modal">{props.children}</div>
}
interface FormProps {
onSubmit: (formValues: FormValues) => void
}
interface FormValues {
username: string
password: string
}
function Form(props: FormProps) {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
return (
<form
onSubmit={e => {
e.preventDefault()
props.onSubmit({
username,
password
})
}}
>
<input
type="text"
placeholder="username"
onChange={e => {
setUsername(e.target.value)
}}
/>
<input
type="text"
placeholder="password"
onChange={e => {
setPassword(e.target.value)
}}
/>
<button type="submit">Submit</button>
</form>
)
}
render(<App />, document.getElementById('root'))
I assumed you are fresh in FE or React world. Propably you do not need that much-nested structure.
CodePudding user response:
There is a special hook in React called useImperativeHandle. You can use it to call child's functions from parent.
You can find out more in the oficcial React documentation.
example of usage
Child Component
Needs to be wrapped into forwardRef like that:
export const ChildComponent = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
async functionName() {
await someLogic();
},
}));
Parent Component
In parent component you need to pass ref to the child. Then you can use child's function this way:
const childRef = useRef(null)
childRef.current.functionName()