How do i listen to children events in parent component?
Look at my code what i am trying to achieve.
Page Component
interface PageProps {}
const Page: FC<PageProps> = ({}) => {
//I don't want to bind handlers here and bind it to Parent.
return (
<div>
<Parent>
<>
<Child />
<Child />
<Child />
<Child />
</>
</Parent>
</div>
)}
export default Page;
Child Component
import { FC } from 'react';
import AnotherChild from '../components';
interface IProps {
handleChange?: (value: string) => void;
}
const Child: FC<IProps> = ({ handleChange }) => {
//Methods
const onChange = (value: string) => {
handleChange ? handleChange(value) : null;
}
return (
<div>
<AnotherChild onChange={onChange} />
</div>
)
}
export default Child;
Parent Component
import React, { FC, Children, cloneElement, isValidElement } from 'react';
interface IProps {
children: React.ReactNode
}
const Parent: FC<IProps> = ({ children }) => {
//Methods
const handleChange = (value: string) => {
console.log("VALUE: ", value)
}
const arrChildren = Children.toArray(children);
return (
<div>
{
arrChildren.map((child, index) => {
return (
React.cloneElement(child, { handleChange: handleChange })
)
})
}
</div>
)
}
export default Parent;
So, I have children components which is emitting handleChange event, I want to listen those events in my <Parent />
component which is wrapped around.
Look at my <Page />
Component that is how those components will be called.
I have tried something you can look at my code but i am not getting those events.
Please help me to figure it out what i was doing wrong here.
Thank you
CodePudding user response:
well, you did have the correct idea for listening to child events.
the problem is that you wrapped the child components inside a React.Fragment
.
I think you did that because of the type of child props. the witch says children: React.ReactNode
. change that to children: React.ReactNode | React.ReactNode[]
and then remove the fragment.
you should have something like this.
interface IProps {
handleChange?: (value: string) => void;
}
const Child: FC<IProps> = ({ handleChange }) => {
const onChange = (value: string) => {
handleChange ? handleChange(value) : null;
}
return (
<div>
<AnotherChild onChange={onChange} />
</div>
)
}
interface IProps {
children: React.ReactNode | React.ReactNode[]
}
const Parent: FC<IProps> = ({ children }) => {
const handleChange = (value: string) => {
console.log("VALUE: ", value)
}
const arrChildren = Array.isArray(children) ? children : [children];
return (
<div>
{
arrChildren.map((child, index) => {
return (
React.cloneElement(child, { handleChange: handleChange })
)
})
}
</div>
)
}
const Page: FC<PageProps> = ({}) => {
return (
<div>
<Parent>
<Child />
<Child />
<Child />
<Child />
</Parent>
</div>
)}
CodePudding user response:
Assuming that the goal is to add a handleChange
to props of the Child
from the wrapper Parent
, according to React document, perhaps try the following way of using cloneElement
in this use case:
Simplified live demo on: stackblitz
Page Component:
interface PageProps {}
const Page: FC<PageProps> = ({}) => {
return (
<div>
<Parent>
{[1, 2, 3, 4].map((item) => (
<Child key={item} />
))}
</Parent>
</div>
);
};
Parent Component:
import React, { FC, Children, cloneElement } from 'react';
interface IProps {
children: React.ReactNode;
}
const Parent: FC<IProps> = ({ children }) => {
const handleChange = (value: string) => {
console.log('VALUE: ', value);
};
return (
<div>
{Children.map(children, (child, index) =>
cloneElement(child as React.ReactElement, {
handleChange: handleChange,
})
)}
</div>
);
};