reactjs - Component không thể được sử dụng như một thành phần JSX. Kiểu trả về của nó 'Element[]' không phải là một phần tử JSX hợp lệ
Lỗi sau xảy ra trong thành phần Todos bên trong 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
Các tệp tương ứng:
TodoApp.tsxfunction TodoApp() {
return (
<Body>
<AppDiv>
<Form />
<Todos />
<Footer />
</AppDiv>
</Body>
);
}
Todos.tsxfunction 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.tsxtype 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>
);
}
};
và giao diện TodoProps được định nghĩa như sau
interface TodoProps {
text: string;
completed: boolean;
id: string;
}
Tôi đã thử sửa lỗi bằng cách bao quanh bản đồ các mục với fragments, nhưng lỗi vẫn xuất hiện. Điều duy nhất hiện tại đang giải quyết được vấn đề là khai báo ở đầu Todos.tsx hàm này là any: function Todos(): any
Nhân tiện: Tôi đang sử dụng StyledComponents, nhưng tôi không nghĩ vấn đề liên quan đến thư viện này.
Câu trả lời #1
Một thành phần cần trả về một phần tử gốc duy nhất. Bạn có thể sử dụng các fragment để đóng gói một mảng các phần tử thành một phần tử duy nhất, bằng cách sử dụng fragment như phần tử gốc duy nhất đó.
Vậy thì cái này không làm gì cả:
function Todos(): JSX.Element {
return todos.map(todo => (
<>
<li>{todo.task}</li>
</>
)
}
Bởi vì bây giờ nó đang trả về một mảng [<><li/></>, <><li/></>, ...]. Đoạn mã đó cần phải là phần tử gốc duy nhất.
Bạn cần sử dụng đoạn mã như sau:
function Todos(): JSX.Element {
return <>{
todos.map(todo => <li>{todo.task}</li>)
}</>
}
Bạn lồng tất cả JSX trả về trong một mảnh duy nhất.
Sử dụng mẫu đó, bạn có thể kết thúc với cái gì đó như thế này:
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 />
Hãy chú ý cách mỗi câu lệnh return chỉ trả về một JSX.Element: chính là fragment.
Sân chơi
Câu trả lời #2
Thêm mã sau
"paths": {
"react": [ "./node_modules/@types/react" ]
}
vào tệp tsconfig.json, trong compilerOptions
Đây là cách mà mã của tôi trông bây giờ:
{
"compilerOptions": {
"jsx":"react",
"strict": true,
"paths": {
"react": [ "./node_modules/@types/react" ]
}
}
}
Nó đang chỉ cho trình biên dịch TypeScript biết rằng khi nó gặp một tham chiếu đến module "react", nó nên tìm triển khai thực tế trong thư mục "./node_modules/@types/react".
Câu trả lời #3
Trong kịch bản khi ai đó đã nâng cấp lên React phiên bản mới hơn, ví dụ, 16 -> 17, điều này có thể bắt đầu xảy ra.
Bây giờ, khi tìm kiếm @types/react trong dự án của bạn, bạn có thể nhận thấy nhiều gói npm có @types/react : "*",. Giải pháp để loại bỏ vấn đề này, trong trường hợp đó, là thêm vào package.json của bạn:
"resolutions": {
"@types/react": "^17.0.38"
}
Câu trả lời số 4
Bạn cần trả về một phần tử JSX, không phải một mảng. Bao toàn bộ component là một giải pháp, nhưng bạn cần thực hiện điều đó bên ngoài hàm map/filter.
// 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, index) => (
<ul key={index}>
<Todo todo={todo} />
</ul>
));
}
</>
}
return (
<>
{todos.map((todo, index) => (
<div key={index}>
<Todo todo={todo} />
</div>
));
}
</>
}
return (
<>{todos.map((_, index) => <div key={index} />)}</>
);
}
Câu trả lời số 5
Trong trường hợp ai đó đang gặp vấn đề với React + Typescript, hãy thử thêm cài đặt dưới đây vào tsconfig.json. Nó đã có hiệu quả với tôi.
"allowSyntheticDefaultImports" : true
Câu trả lời số 6
Tôi đang gặp phải cùng một vấn đề về lỗi này. Tôi đã thêm đoạn mã dưới đây vào tệp package.json của mình.
"resolutions": {
"@types/react": "17.0.2",
"@types/react-dom": "17.0.2",
},
Sau đó, chạy yarn install trên terminal.
Nó hiệu quả với tôi.
