1
<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.

user9630194
  • 388
  • 2
  • 10
  • You can't really, and more importantly you shouldn't want to. If you think you need to do this you need to rethink your design and/or approach. – super Nov 14 '22 at 23:38
  • @super you can do it, some libraries do that although I'm not a fan https://www.npmjs.com/package/google-map-react - look how markers are added – Konrad Nov 14 '22 at 23:40
  • @KonradLinkowski You random link seems fairly irrelevant in this context. – super Nov 14 '22 at 23:45
  • https://stackoverflow.com/a/73378294/5089567 – Konrad Nov 14 '22 at 23:46
  • @KonradLinkowski Cloning the element means you can extract the props. But it not the same as accessing them. Which you still can't do. Sure, you can do anything if you just go far enough out of your way. – super Nov 14 '22 at 23:49
  • Does this answer your question? [Accessing React component children props from the parent](https://stackoverflow.com/questions/29670173/accessing-react-component-children-props-from-the-parent) – Henry Woody Nov 14 '22 at 23:57

2 Answers2

2

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

Benjamin
  • 3,428
  • 1
  • 15
  • 25
1

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
Konrad
  • 21,590
  • 4
  • 28
  • 64