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)
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
CodePudding user response:
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
}
CodePudding user response:
...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
.