英文:
Array is not iterable in useReducer()
问题
在练习React hooks时,我使用ReactTS编写了一个简单的待办事项网站。我遇到了一个问题,即尽管在State
类型中已经定义为TodoProps[]
,但我不断收到一个错误,即我的待办事项数组不可迭代。
错误通常发生在“添加”按钮点击之后。
错误:state.todos 不可迭代
如果我写console.log(state.todos)
,我会收到undefined。
我在这里检查了代码,它似乎与我的代码相同,但无论如何我无法解决这个问题。
(错误位置在代码中有标记)
TodoProps:
export interface TodoProps {
title: string
id: number
todoKey?: number
completed: boolean
}
LayoutContext.tsx:
import { createContext, useState, ReactNode } from 'react';
export const TodoListContext = createContext<any>({});
export const TitleContext = createContext<any>({});
type LayoutContextProps = {
children: ReactNode
}
export const LayoutContext = ({ children }: LayoutContextProps): JSX.Element => {
const [title, setTitle] = useState<string>('');
const [todoList, setTodoList] = useState<object[]>([
{ title: 'Todo 1', id: 418759 },
{ title: 'Todo 2', id: 440123 }
]);
return (
<TitleContext.Provider value={{ title, setTitle }}>
<TodoListContext.Provider value={{ todoList, setTodoList }}>
{children}
</TodoListContext.Provider>
</TitleContext.Provider>
);
}
Layout.tsx:
import { Button, Todos, Input } from 'Components';
import { LayoutContext } from 'Context/LayoutContext';
import cn from 'classnames';
import styles from './Layout.module.scss';
export const Layout = (): JSX.Element => {
return (
<section className={cn(styles.layout)}>
<LayoutContext>
<Todos/>
<Input/>
<Button>Add</Button>
</LayoutContext>
</section>
);
}
Button.tsx:
import styles from './Button.module.scss';
import { ButtonHTMLAttributes, DetailedHTMLProps, ReactNode, useReducer, useContext, useEffect } from 'react';
import { TodoListContext, TitleContext } from '../../Context/LayoutContext';
import { TodoProps } from 'Components/TodoComponent/Todo';
interface ButtonProps extends DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
children: ReactNode
}
export const Button = ({ children }: ButtonProps): JSX.Element => {
const { todoList } = useContext(TodoListContext);
const { title, setTitle } = useContext(TitleContext);
type State = { todos: TodoProps[] };
type Action = { type: 'addTodo', payload: string };
const reducer = (state: State, action: Action) => {
switch (action.type) {
case 'addTodo':
const newTodoCard: TodoProps = {
title: action.payload,
id: Math.floor(Math.random() % 10),
completed: false
};
console.log(todoList);
console.log(state.todos);
return {
// 这里出错
todos: [...state.todos, newTodoCard]
};
}
}
const [state, dispatch] = useReducer(reducer, todoList);
const handleAdd = (e: any) => {
e.preventDefault();
dispatch({ type: 'addTodo', payload: title });
setTitle('');
}
return (
<button
type='submit'
onClick={handleAdd}
className={styles.button}
>{children}</button>
);
}
不知道是否还可以提供更多信息。
英文:
To practice React hooks I wrote a simple todo site with ReactTS. I faced a problem that I keep receiving an error that my todos array is not iterable, despite it is defined as TodoProps[]
in State
type
Error occurs logically after 'add' button click
Error: state.todos is not iterable
If I write console.log(state.todos)
I receive undefined
I checked code on medium and it seems to be identical to mine, but anyway I cannot resolve this issue.
(Error place marked in code)
TodoProps:
export interface TodoProps {
title: string
id: number
todoKey?: number
completed: boolean
}
LayoutContext.tsx:
import { createContext, useState, ReactNode } from 'react'
export const TodoListContext = createContext<any>({})
export const TitleContext = createContext<any>({})
type LayoutContextProps = {
children: ReactNode
}
export const LayoutContext = ({ children }: LayoutContextProps): JSX.Element => {
const [title, setTitle] = useState<string>('')
const [todoList, setTodoList] = useState<object[]>([
{title: 'Todo 1', id: 418759},
{title: 'Todo 2', id: 440123}
])
return (
<TitleContext.Provider value={{ title, setTitle }}>
<TodoListContext.Provider value={{ todoList, setTodoList }}>
{children}
</TodoListContext.Provider>
</TitleContext.Provider>
)
}
Layout.tsx:
import { Button, Todos, Input } from 'Components'
import { LayoutContext } from 'Context/LayoutContext'
import cn from 'classnames'
import styles from './Layout.module.scss'
export const Layout = (): JSX.Element => {
return (
<section className={cn(styles.layout)}>
<LayoutContext>
<Todos/>
<Input/>
<Button>Add</Button>
</LayoutContext>
</section>
)
}
Button.tsx:
import styles from './Button.module.scss'
import { ButtonHTMLAttributes, DetailedHTMLProps, ReactNode, useReducer, useContext, useEffect } from 'react';
import { TodoListContext, TitleContext } from '../../Context/LayoutContext';
import { TodoProps } from 'Components/TodoComponent/Todo';
interface ButtonProps extends DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
children: ReactNode
}
export const Button = ({ children }: ButtonProps): JSX.Element => {
const { todoList } = useContext(TodoListContext)
const { title, setTitle } = useContext(TitleContext)
type State = { todos: TodoProps[] }
type Action = { type: 'addTodo', payload: string }
const reducer = (state: State, action: Action) => {
switch (action.type) {
case 'addTodo':
const newTodoCard: TodoProps = {
title: action.payload,
id: Math.floor(Math.random() % 10),
completed: false
}
console.log(todoList)
console.log(state.todos)
return {
Error here
\/\/\/\/\/\/\/\/\/\/\/\/\/\/
todos: [...state.todos, newTodoCard]
/\/\/\/\/\/\/\/\/\/\/\/\/\/\
}
}
}
const [state, dispatch] = useReducer(reducer, todoList)
const handleAdd = (e: any) => {
e.preventDefault()
dispatch({ type: 'addTodo', payload: title})
setTitle('')
}
return (
<button
type='submit'
onClick={handleAdd}
className={styles.button}
>{children}</button>
)
}
Don't know if I can add any more information..
答案1
得分: 3
将数组传递给你的 reducer 的初始状态,然后在 reducer 中将其访问为对象,所以你只需要更改以下这些行:
从
const [state, dispatch] = useReducer(reducer, todoList)
到
const [state, dispatch] = useReducer(reducer, { todos: todoList });
然后你就可以继续进行。
英文:
You are passing an array to the initial state into your reducer and accessing it as an object in your reducer so you just need to change these lines
from
const [state, dispatch] = useReducer(reducer, todoList)
To
const [state, dispatch] = useReducer(reducer, { todos: todoList });
and you are good to go.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论