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..
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.