简体   繁体   中英

Accessing child.type.name in React Typescript

I'm using React Functional components throughout a Typescript application.

I'm trying to dynamically update some child components in another component, as below:

const memoizedIterateAndAddProps = useCallback(
    (kids: ReactNode) => {
      return React.Children.map(kids, (child) => {
        if (React.isValidElement(child)){
           console.log(child.type.name)
          }
      }
  }, [])

but typescript keeps complaining with Property 'name' does not exist on type 'string | JSXElementConstructor<any>'. Property 'name' does not exist on type 'string'.ts(2339) Property 'name' does not exist on type 'string | JSXElementConstructor<any>'. Property 'name' does not exist on type 'string'.ts(2339)

I get this is related to the fact that a child could just be a string, but I can't figure out a way of solving this that makes Typescript happy.

I'd be incredibly grateful for any help from anyone

...but I can't figure out a way of solving this that makes Typescript happy.

Check that it isn't a string:

const memoizedIterateAndAddProps = useCallback(
    (kids: ReactNode) => {
        return React.Children.map(kids, (child) => {
            if (React.isValidElement(child) && typeof child.type !== "string") {
//                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
               console.log(child.type.name);
            }
        });
    }
);

(Or && "name" in child.type would work too.)

You get a string for type when you do <div/> and such, so you have to filter out that case if you want to use name .

React.isValidElement has this type signature:

function isValidElement<P>(object: {} | null | undefined): object is ReactElement<P>;

Above typeguard, narrows child to be ReactElement which has this type signature:

interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
        type: T;
        props: P;
        key: Key | null;
    }

As you might have noticed, type is a union of string and JSXElementConstructor<any> .

Hence, if you want to make sure that you have a deal with function, you should use custom typeguard:

const isFunction=(data:any):data is (...args:any[])=>any => typeof data === 'function'

WHole code:

import React, { useCallback, ReactNode } from 'react'
const isFunction = (data: any): data is (...args: any[]) => any => typeof data === 'function'

const App = () => {
  const memoizedIterateAndAddProps = useCallback(
    (kids: ReactNode) => {
      return React.Children.map(kids, (child) => {
        if (React.isValidElement(child) && isFunction(child.type)) {
          console.log(child.type.name)
        }
      }
  }, [])

  return null
}

Playground

import { isValidElement } from 'react'

if (isValidElement(child) && typeof child.type === 'function') {
  console.log(child.type.name) // safe access to name
}

Checks that:

  • child is a ReactElement , and react elements have type property.
  • type is a function , and functions have name property.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM