Home > Software design >  How do I pass down components spread props with TypeScript?
How do I pass down components spread props with TypeScript?

Time:12-19

EDIT: I focused too much on TypeScript that I didn't realize I wasn't mapping the data. It works now.

I am just starting to use TypeScript, and I can't figure out how to pass down props with the spread operator. How should I do this? from what I understand I shouldn't be using React.FC so I am trying to do it with interface, but still don't understand how to get the props. Thank you

Index.tsx:

    const Home: NextPage = () => {
        const [data, setData] = useState([]);
        console.log(data)
    
        useEffect(() => {
            fetch('https://jsonplaceholder.typicode.com/posts/')
                .then((response) => response.json())
                .then((json) => setData(json));
        }, []);
    
        return (
            <div className={styles.container}>
                <CustomComponent {...data} />
            </div>
        );
    };

export default Home;

CustomComponent.tsx:

    interface Props {
    userId: number;
    id: number;
    title: string;
}

const CustomComponent = ({ userId }: Props) => {
    console.log(userId);
    return <div>{userId}</div>;
};

export default CustomComponent;

CodePudding user response:

You're trying to use spread on an array (data) in a place where only property spread is supported (the props list of a JSX element expression). Although arrays are objects, their property names are "0" and such, not "userId" and such, so that doesn't match the props expected by the component.

You've said in a comment that you aren't trying to do anything in particular, just trying to see how to do what you're doing. Your <CustomComponent {...data}> is just fine if data is an object, but it's an array.

It looks like your data source returns this:

[
    {
        "userId": 1,
        "id": 1,
        "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
        "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
    },
    ...
]

If so, you probably want an array of CustomComponent, and then use ... to spread out each element of data into the props of each CustomComponent.

To do that, you have to set the type of data correctly. Define an interface/object type:

interface PostInfo {
    userId: number;
    id: number;
    title: string;
}

then use an array of those in the setState type arggument so TypeScript knows what's in the array:

const [data, setData] = useState<PostInfo[]>([]);
//                              ^^^^^^^^^^^^

then render an array of components:

return (
    <div className={styles.container}>
        {data.map(post => <CustomComponent key={post.id} {...post} />)}
    </div>
);

Here's a live example with the TypeScript parts commented out:

const {useState, useEffect} = React;

const styles = {
    container: "container",
};

/*
interface PostInfo {
    userId: number;
    id: number;
    title: string;
}
*/
const Home/*: NextPage*/ = () => {
    const [data, setData] = useState/*<PostInfo[]>*/([]);
    console.log(data)

    useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/posts/')
            .then((response) => {
                if (!response.ok) {
                    throw new Error(`HTTP error ${response.status}`);
                }
                return response.json();
            })
            .then((data) => setData(data/* as PostInfo[]*/));
    }, []);

    return (
        <div className={styles.container}>
            {data.map(post => <CustomComponent key={post.id} {...post} />)}
        </div>
    );
};

/*
interface Props {
    userId: number;
    id: number;
    title: string;
}
*/

const CustomComponent = ({ userId, id, title }/*: Props*/) => {
    return <div>userId: {userId}, id: {id}, title: {title}</div>;
};

ReactDOM.render(<Home />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

And the full TypeScript (playground link):

import React, {useState, useEffect} from "react";

// (Stand-in)
const styles = {
    container: "container",
};

interface PostInfo {
    userId: number;
    id: number;
    title: string;
}
const Home/*: NextPage*/ = () => {
    const [data, setData] = useState<PostInfo[]>([]);
    //                              ^^^^^^^^^^^^
    console.log(data)

    useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/posts/')
            .then((response) => {
                if (!response.ok) {
                    throw new Error(`HTTP error ${response.status}`);
                }
                return response.json();
            })
            .then((data) => setData(data as PostInfo[]));
    }, []);

    return (
        <div className={styles.container}>
            {data.map(post => <CustomComponent {...post} />)}
        </div>
    );
};

interface Props {
    userId: number;
    id: number;
    title: string;
}

const CustomComponent = ({ userId, id, title }: Props) => {
    return <div>userId: {userId}, id: {id}, title: {title}</div>;
};
  • Related