Typescript error when rendering a component that returns array in React

Asked

Viewed 428 times

1

I’m creating an React app with Typescript that searches the Github API.

I’m having trouble passing one array as props to another component.

The logic would be: User has Repos (array of Github repositories), which in turn has RepoItem (individual object of array).

But I’m getting the following error:

JSX element type 'Element[]' is not a constructor function for JSX elements.
Type 'Element[]' is missing the following properties from type 'Element': type, props, key  TS2605

Filing cabinet App.tsx:

return (
    <User
        user={user}
        repos={repos}
        loading={loading}
    />
);

Filing cabinet User.tsx:

The error occurs specifically in the following excerpt: <Repos repos={repos} />, in the method render of the component User.

import React, { Component, Fragment } from "react";
import Repos from "../repos/Repos";
import { RouteComponentProps } from "react-router";


interface IRepos {
    id: string;
    name: string;
    html_url: string;
}

interface IUser {
    user: any;
    loading: boolean;
    repos: IRepos[];
}

class User extends Component<IUser & RouteComponentProps> {
    render() {
        const { loading, repos } = this.props;

        if (loading) {
            return <Spinner />;
        } else {
            return (
                <Fragment>
                    <Repos repos={repos} />
                </Fragment>
            );
        }
    }
}

export default User;

Filing cabinet Repos.tsx:

import React from "react";
import PropTypes from "prop-types";
import RepoItem from "./RepoItem";

interface IRepos {
    id: string;
    name: string;
    html_url: string;
}

interface IRepoWrapper {
    repos: IRepos[];
}
const Repos = ({ repos }: IRepoWrapper) => {
    return repos.map(repo => <RepoItem repo={repo} key={repo.id} />);
};

Repos.propTypes = {
    repos: PropTypes.array.isRequired
};

export default Repos;

Filing cabinet RepoItem.tsx:

import React from "react";
import PropTypes from "prop-types";

interface IRepos {
    repo: {
        id: String;
        name: String;
        html_url: string;
    };
}

const RepoItem = ({ repo }: IRepos) => {
    return (
        <div className='card'>
            <h3>
                <a href={repo.html_url}>{repo.name}</a>
            </h3>
        </div>
    );
};

RepoItem.propTypes = {
    repo: PropTypes.object.isRequired
};

export default RepoItem;

I did a test bringing RepoItem straight to User, and then using map to return several <RepoItem /> and it worked. But I’d like to do it within Repos, and not within User.

  • Who denied the question could leave a comment on the reason? I would like to improve my question so that it can receive a good answer :)

  • I gave a decrease in the code "useless" and I made clearer where is the error, I hope it helps.

  • 1

    I think the problem is the value that component Repos is returning. Probably, for some reason, React expects an object (React Element), but is receiving an array. Try to involve the return of this component in a Fragment. Probably solves.

  • @Luizfelipe Repos is returning a array.map() right? You want me to encapsulate the array.map() within a Fragment? I believe I cannot call my function map within an element JSX. I’m not getting it right

  • 1

    You can use the map within JSX yes... For this, use {} to evaluate expressions within JSX. See the published response now.

1 answer

2


Basically, it’s a Typescript error.

As you can see in the React documentation, there is nothing wrong with rendering a component that returns an array of elements (depending on its components Repos is doing). A documentation makes it explicit that arrays and Fragments can be returned from components:

Arrays and Fragments. Let you Return Multiple Elements from render. See the Documentation on Fragments for more Details.

However, the Typescript type definition only considers valid the following types of elements (to be returned by a component):

  • ReactChild;
  • ReactFragment;
  • ReactPortal;
  • boolean;
  • null;
  • undefined.

See this definition of types.

Therefore, even if it is valid by React, Typescript simply will not allow you to do the surrender of a component that returns a array. It may even be an error of definitions, who knows.

Therefore, to fix, you must create components that return only some of the types I listed above. In your case, a ReactFragment seems to me to be the most ideal.

So just fix your component Repos to return something like this:

const Repos = ({ repos }: IRepoWrapper) => {
  return (
    <React.Fragment>
      {repos.map(repo => <RepoItem repo={repo} key={repo.id} />)}
    </React.Fragment>
  );
};

If you are using create-react-app or any other environment you support, you can use the short notation of fragments.

  • Thank you so much for the detailed answer, I really tested it here and it worked accordingly! It would be interesting to know if there is any greater reason for this "limitation", or if it is really something that was not thought out at the time of implementation.

  • 1

    It’s probably lack of synchrony between who manages the type definitions and the Core Team at React. There must even be a Issue on Github because of this, and if they don’t tidy up at some point it should be expected behavior. I confess that I also did not understand this lack of sync. But I must say that I no longer find it necessary for a component to return a array directly. This was much used before the introduction of the fragments. With them introduced, the arrays as return are rarely needed. I think it is still something "valid" for not breaking legacy code.

  • 1

    I get it, it makes sense. I’m still starting in typescript and I don’t fully understand why things... But your reply has already helped me a great deal in this regard, thank you very much :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.