Component cannot be used as a JSX component. Its return type 'Element[]' is not a valid JSX element
ReactjsTypescriptReactjs Problem Overview
I'm currently getting the following error on the Todos
component inside TodoApp.tsx
: 'Todos' cannot be used as a JSX component.
Its return type 'Element[]' is not a valid JSX element.
Type 'Element[]' is missing the following properties from type 'Element': type, props, key
And this is my folder structure
TodoApp.tsx
function TodoApp() {
return (
<Body>
<AppDiv>
<Form />
<Todos />
<Footer />
</AppDiv>
</Body>
);
}
Todos.tsx
function Todos(): JSX.Element[] {
const todos = useSelector((state: RootState) => state.todos);
const footer = useSelector((state: RootState) => state.footer);
if (footer.hideAll) {
if (footer.showCompleted) {
return todos
.filter((todo) => !todo.completed)
.map((todo: any) => (
<>
<ul>
<Todo todo={todo} />
</ul>
</>
));
}
return todos.map((todo) => (
<>
<div>
<Todo todo={todo} />
</div>
</>
));
}
return todos.map(() => (
<>
<div></div>
</>
));
}
Todo.tsx
type Todo = {
todo: TodoProps;
};
const Todo = ({ todo }: Todo) : JSX.Element => {
const [isEditing, edit] = useState<boolean>(false);
const dispatch = useDispatch();
if (!isEditing) {
return (
<TodoDiv>
<Li
key={todo.id}
completed={todo.completed}
onClick={() => dispatch(completeTodo(todo.id))}
// style={{
// textDecoration: todo.completed ? "line-through" : "none"
// }}
>
{todo.text}
</Li>
<TodoBttns>
<Button edit onClick={() => edit(!isEditing)}>
<img src={editBttn} alt="Edit Button" />
</Button>
<Button delete onClick={() => dispatch(deleteTodo(todo.id))}>
<img src={deleteBttn} alt="Delete Button" />
</Button>
</TodoBttns>
</TodoDiv>
);
} else {
return (
<FormEdit>
<InputForm key={todo.id} {...{ todo, edit }} />
</FormEdit>
);
}
};
and the TodoProps interface is the following:
interface TodoProps {
text: string;
completed: boolean;
id: string;
}
already tried the fix of wraping the map items with fragments, but I still can't make it work. The only thing that as of now is fixing the issue is declaring at the top of Todos.tsx
as this function Todos(): any
As a side note: I'm using Styled Components, but I don't think the issue is related to the library.
Reactjs Solutions
Solution 1 - Reactjs
A component needs to return a single root element. You can use fragments to package an array of elements as a single element, by using the fragment as that single root element.
So this does nothing:
function Todos(): JSX.Element {
return todos.map(todo => (
<>
<li>{todo.task}</li>
</>
)
}
Because it's now returning an array of [<><li/></>, <><li/></>, ...]
. That fragment needs to be the single root element.
You need to use the fragment like this:
function Todos(): JSX.Element {
return <>{
todos.map(todo => <li>{todo.task}</li>)
}</>
}
You nest all returned JSX in one single fragment.
Using that pattern you may end up with somehting like this:
function Todos(): JSX.Element {
const todos = useSelector((state: RootState) => state.todos);
const footer = useSelector((state: RootState) => state.footer);
if (footer.hideAll) {
if (footer.showCompleted) {
return <>{
todos
.filter((todo) => !todo.completed)
.map((todo: any) => (
<ul>
<Todo todo={todo} />
</ul>
))
}</>
}
return <>{
todos.map((todo) => (
<div>
<Todo todo={todo} />
</div>
))
}</>
}
return <>{
todos.map(() => (
<div></div>
))
}</>
}
// Works without error
<Todos />
Note how each return statement returns just one JSX.Element
: the fragment.
Solution 2 - Reactjs
You need to return a JSX Element, not an array. Wrapping the whole component is a solution, but you need to do it outside of the map/filter function.
Todos.tsx
function Todos(): JSX.Element {
const todos = useSelector((state: RootState) => state.todos);
const footer = useSelector((state: RootState) => state.footer);
if (footer.hideAll) {
if (footer.showCompleted) {
return (
<>
{todos.filter((todo) => !todo.completed).map((todo: any) => (
<ul>
<Todo todo={todo} />
</ul>
));
}
</>
}
return (
<>
{todos.map((todo) => (
<div>
<Todo todo={todo} />
</div>
));
}
</>
}
return (
<>{todos.map(() => <div />)}</>
);
}
Solution 3 - Reactjs
In my case, it was a forgotten import. Basically copy-pasted some of my code and forgot to import one of the components and this was the error message I get.
Solution 4 - Reactjs
In case anyone is facing issue with React + Typescript stack, try adding below setting in tsconfig.json. It worked for me.
"allowSyntheticDefaultImports" : true