2

I'm building a REACT App (with TypeScript), and I'm facing a problem which solution would require the possibility to identify the different REACT children components. Basically, something like this:

const RibbonTab: ReactFC<IRibbonTab> = (props) => {
 return <p>{props.header}</p>
}

const Ribbon: ReactFC<IRibbon> = (props) => {
  const ribbonTabs = props.children.ofType<RibbonTab>(); // This is what I'd like to accomplish.

  return <>props.children</>;
}

I've searched for a while, and many, if not all the accepted answers (like here: only allow children of a specific type in a react component), are to use type.displayName (this.props.children[child].type.displayName). Unfortunately, it has also been said that displayName might be minified in production, so I just can't rely on that.

The only way I found out is to "force" a own display name. Basically, I create an interface IUniqueComponent and then extends it for each component that should be identifiable. Something like this:

interface IUniqueComponent {
  displayNameForIdentification: string;
}

const RibbonTab: ReactFC<IRibbonTab extends IUniqueComponent> = (props) => { ... }
RibbonTab.defaultProps = { displayNameForIdentification: "RibbonTab" }

And then, using React.Children.forEach, I can filter the children:

React.Children.forEach(props.children, (c) => { 
  if (React.isValidElement(c)) {
    if (c.props.displayNameForIdentification === 'WHAT I WANT') { ... }
  }
})

What I don't like of this solution is the fact that I'm adding the identifier by myself, and even the props name, displayNameForIdentification, is not saved by React somewhere safe, but it's just a prop: I've no certainty that a developer override that props (OK, I could just say "NEVER USE displayNameForIdentification PROPS", but I'd like to avoid this).

So, is there any way to identify the type of REACT children component?


EDIT: By following Ramesh suggestion, I can write something like this:

React.Children.forEach(props.children, (c) => { 
  if (React.isValidElement(c)) {
    if (c.type.name === 'WHAT I WANT') { ... }
  }
})

Though, TypeScript does not like c.type.name, it gives me the following error, and I'm not able to solve it:

Property 'name' does not exist on type 'string | ((props: any) => ReactElement Component)>) | (new (props: any) => Component)'. Property 'name' does not exist on type 'string'.ts(2339)

Any suggestion on this newcomer?

Jolly
  • 1,678
  • 2
  • 18
  • 37

2 Answers2

2

Use type.name This will return the name of the function

import * as React from "react";
import { render } from "react-dom";


class App extends React.Component<{}> {
  public render() {
    return (
      <div>
      </div>
    );
  }
}

const app = <App/>

console.log(app.type.name==="App")

render(app, document.getElementById("root"));
  • 1
    Is this safe? I mean, it's REACT related, or JavaScript related? Because I know it's not possible getting a function name with a standardized method, so I'm afraid of this solution. Thank though! – Jolly Dec 05 '19 at 13:01
  • 1
    This is a js related function. See browser support:- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name – Ramesh Kithsiri HettiArachchi Dec 05 '19 at 13:03
  • Working for js component classes and component functions. – Ramesh Kithsiri HettiArachchi Dec 05 '19 at 13:04
  • :o I wasn't aware of that, thanks. Though, using this I've a problem with TypeScript (I've edited the question above) – Jolly Dec 05 '19 at 13:12
  • 1
    @Jolly It depends on your children's type. As your question use `if(typeof children!=='string' && children.type.name==="App")` Because string has not a property called type. – Ramesh Kithsiri HettiArachchi Dec 05 '19 at 13:22
  • Nice! I have written `React.isValidElement(child) && typeof(child.type) !== 'string`: this way, I can access `child.type.name`. – Jolly Dec 05 '19 at 13:31
0

You could do this:

{children && children.every(child => child.type.displayName === "Tab")

And component:

const Tab = (props) => {...}

Tab.displayName = "Tab"

This is just my example, it renders only components that have name "Tab"

Merim
  • 1,283
  • 2
  • 21
  • 36
  • As I explained, I don't wanna use `displayName` since it could be minified in production mode (which means, all functionality based on it would just break as well) – Jolly Dec 05 '19 at 13:02