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.
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.
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 />)}</>
);
}
In case anyone is facing issue with React + Typescript stack, try adding below setting in tsconfig.json. It worked for me.
"allowSyntheticDefaultImports": true
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.
There's a possibility that you have installed (and probably failed) a new package, which could also cause this error.
If that's the case (that you have recently installed and failed or cancelled a package), you can delete node_modules
and run npm i
or yarn install
then try again.
You need to install types for react npm install @types/react
or yarn add @types/react
Sometimes this error can occur when you use the switch case
syntax to render your component.
For example:
switch (loading) {
case "pending":
return <span>Pending</span>;
case "succeeded":
return <span>Succeeded</span>;
case "failed":
return <span>Failed</span>;
Sometimes your loading status can mistakenly have a value that you didn't specify in the case
, so your component will return undefined
. Therefore, you should add a default
value:
default: return <span>¯\_(ツ)_/¯</span>;
For a scenario when someone has upgraded to later React
, eg, 16
-> 17
it could start happening.
Now, while looking for @types/react
in your project, you could notice many npm
packages have @types/react: "*",
. The solution to get rid off the problem, in that scenario, would be to add into your package.json
:
"resolutions": {
"@types/react": "^17.0.38"
}
I'm facing the same issue about this error. I add the below code to my package.json file.
"resolutions": {
"@types/react": "17.0.2",
"@types/react-dom": "17.0.2",
"graphql": "^16.5.0"
},
Then, Run yarn install
on a terminal.
It works for me.
This answer is related to same problem on react-native. Adding typescript to my newly created react-native app and moving App.js to App.tsx, I met the same error for component named Section. The solution for me was a component type React.Fc<{...}>
. This is the problematic Section component after the fix when all errors disappeared and my app started as expected.
import React, { ReactNode } from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
const Section: React.FC<{
children: ReactNode;
title: string;
}> = ({children, title}) => {
const isDarkMode = useColorScheme() === 'dark';
return (
<View style={styles.sectionContainer}>
<Text
style={[
styles.sectionTitle,
{
color: isDarkMode ? Colors.white : Colors.black,
},
]}>
{title}
</Text>
<Text
style={[
styles.sectionDescription,
{
color: isDarkMode ? Colors.light : Colors.dark,
},
]}>
{children}
</Text>
</View>
);
};
Another common cause of the "Component cannot be used as a JSX component" error is when we return anything other than a JSX element or null from a component, or forget to return a value.
The App component returns undefined because we have placed our return statement on one line and the JSX code on the next without using parentheses .
We aren't allowed to return undefined from a component, so the error occurs.
Add the following code
"paths": {
"react": [ "./node_modules/@types/react" ]
}
to tsconfig.json
file, in compilerOptions
Here is how my code looks like now:
{
"compilerOptions": {
"jsx":"react",
"strict": true,
"paths": {
"react": [ "./node_modules/@types/react" ]
}
}
}
It is telling the typescript compiler that when it encounters a reference to the "react" module, it should look for the actual implementation in the "./node_modules/@types/react" directory.
In my case I forgot to put back ticks after styled(PreviousStyledElement);
After I put them the code started to work.
export const MyNewStyledElement = styled(PreviousStyledElement)``;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.