<MyComponent>
<button>Click me</button>
</MyComponent>
interface MyComponentProps {
children: ???;
}
const MyComponent: FC<MyComponentProps> = ({children}) => {
const string = children.??? //I want the string to be "Click me"
}
I tried so many combinations but I always get undefined, thank you so much for your help.
CodePudding user response:
As others have pointed out, you probably shouldn't be accessing the props of a child, as it is an anti-pattern in React. However, it is possible.
Let's do it right, and write a function to do so safely.
First a few type predicate helpers.
const isElement = (child: React.ReactNode): child is React.ReactElement =>
(child as React.ReactElement)?.props !== undefined;
const isLeaf = (child: React.ReactNode): child is React.ReactText =>
typeof child === 'string' || typeof child === 'number';
Then a recursive function, that recursively maps over children, picking out string and numbers. Because children can be an array, we return an array of strings or numbers.
function extractText(children: React.ReactNode): React.ReactText[] {
return React.Children.toArray(children).reduce<React.ReactText[]>((previous, child) => {
if (isElement(child)) {
return [...previous, ...extractText(child.props.children)];
}
if (isLeaf(child)) {
return [...previous, child];
}
return previous;
}, []);
}
Finally, put it all together.
const MyComponent = ({ children }: { children: React.ReactNode }): JSX.Element => {
const string = extractText(children)[0];
console.log(string); // Click me
return <>{children}</>;
};
export default function App() {
return (
<MyComponent>
<button>Click me</button>
</MyComponent>
);
}
Link to Sandbox
CodePudding user response:
You want to type your children
as ReactElement
You will then access props by children.props
<MyComponent>
<button>Click me</button>
</MyComponent>
interface MyComponentProps {
children: ReactElement;
}
const MyComponent: FC<MyComponentProps> = ({children}) => {
const string = children.props.children
}
sandbox with the code:
import { ReactElement } from "react";
import "./styles.css";
interface MyComponentProps {
children: ReactElement;
}
const MyComponent = ({ children }: MyComponentProps) => {
const string = children.props.children;
return <>{string}</>;
};
export default function App() {
return (
<MyComponent>
<button>Click me</button>
</MyComponent>
);
}
I created a little different sandbox
import { ReactElement, ReactNode } from "react";
import "./styles.css";
type ChildProps = {
value: string;
children: ReactNode;
};
const Child = (props: ChildProps) => {
return (
<>
the child component ={props.value} {props.children}
</>
);
};
type ComponentProps = {
children: ReactElement;
};
const Component = ({ children }: ComponentProps) => {
const { value, children: nestedChildren } = children.props;
return (
<>
<p>value = {value}</p>
<p>nested children = {nestedChildren}</p>
<p>children = {children}</p>
</>
);
};
export default function App() {
return (
<div className="App">
<Component>
<Child value="test">some inner text</Child>
</Component>
</div>
);
}
Output:
value = test
nested children = some inner text
children = the child component =test some inner text