简体   繁体   中英

React Typescript Problem with injected props

How do I avoid type checking on props injected with React.children.map to a component that I am directly rendering in typescript?

type ContainerProps = {
    children: JSX.Element[];
};
export const Container: React.FC<ContainerProps> = ({ children }) => {
    const [isOpen, setIsOpen] = useState<boolean>(false);

    return (
        <React.Fragment>
            {React.Children.map(children, child =>
                React.cloneElement(child, {
                    isOpen,
                    setIsOpen
                })
            )}
        </React.Fragment>
    );
};
type ClickProps = {
    children: React.ReactNode;
    isOpen: boolean;
    setIsOpen: (boolean) => void;
};
export const Click: React.FC<ClickProps> = ({ children, isOpen, setIsOpen }) => {
    return (
        <div onClick={() => setIsOpen(!isOpen)}>
            {children}
        </div>
    );
};

In this scenario when I call the following code I get the following error because the isOpen and setIsOpen props are not passed in. How do I have type safety of these props inside the Click component while not needing to pass them in when calling the click component directly?

<Container>
    <Click>
        <h1>Test</h1>
    </Click>
</Container>

ERROR
Type '{ children: Element; }' is missing the following properties from type 'ClickProps': isOpen, setIsOpents(2739)

How do I have type safety of these props inside the Click component while not needing to pass them in when calling the click component directly?

The problem here is that when you do:

    <Click>
        <h1>Test</h1>
    </Click>

You are actually rendering that component, turning it into JSX fragments. It later gets cloned, but that doesn't matter for this initial render. And this component has some required props: isOpen and setIsOpen .

It's like calling a function with missing arguments. The invocation is invalid.

I think the best you can do with this setup is to make the props optional, and then bail on rendering unless the props are provided.

type ClickProps = {
    children: React.ReactNode;
    isOpen?: boolean;
    setIsOpen?: (isOpen: boolean) => void;
};
export const Click: React.FC<ClickProps> = ({ children, isOpen, setIsOpen }) => {
    if (isOpen === undefined || setIsOpen === undefined) return null
    return (
        <div onClick={() => setIsOpen(!isOpen)}>
            {children}
        </div>
    );
};

This way, rendering the component without the props is a valid invocation.

Playground


I'm not really sure how you get type safety with React.cloneElement , though. Once you cast the children as JSX.Element[] then typescript no longer knows the original component that came from, so I'm not sure how it could know which props to enforce.

I'm not sure exactly what you are trying to do here, but I would rethink your overall strategy here and see if you can do this without React.cloneElement .

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