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.
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.
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.
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.
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.
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
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 />)}</> ); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With