Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Component cannot be used as a JSX component. Its return type 'Element[]' is not a valid JSX element

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.

like image 509
Ramiro Herrera Avatar asked Jul 02 '20 18:07

Ramiro Herrera


People also ask

How do you fix Cannot be used as a JSX component?

The issue is that we're returning an array of JSX elements instead of a single JSX element. To solve the error, we have to wrap the array using a React fragment or a div element.

What is type of JSX element?

JSX is an embeddable XML-like syntax. It is meant to be transformed into valid JavaScript, though the semantics of that transformation are implementation-specific.

Does not have any construct or call signatures?

The error "JSX element type does not have any construct or call signatures" occurs when we try to pass an element or a react component as props to another component but type the prop incorrectly. To solve the error, use the React. ElementType type.

Which of the following is a way to handle data in React JS?

There are basically two ways in which the data gets handled in React. It gets handled through state and props. In another way, it can be said that two types of Model in React are there to manage the data for the components. Both state and props are plain JavaScript objects.


2 Answers

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.

Playground

like image 51
Alex Wayne Avatar answered Sep 26 '22 01:09

Alex Wayne


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 />)}</>   ); } 
like image 25
himayan Avatar answered Sep 25 '22 01:09

himayan