【问题标题】:TypeError: todos.map is not a functionTypeError: todos.map 不是函数
【发布时间】:2020-08-30 15:32:43
【问题描述】:

我正在使用带有钩子的 React、Redux 和 TypeScript 构建一个待办事项应用程序。

我不知道为什么它可以成功编译但无法在浏览器中运行。

错误:

TypeError: todos.map is not a function.

我检查了todo 的类型,它是Todo[] 类型(即一个数组),相应地,它可以使用map 进行迭代。

我不知道如何解决这个问题。

此处代码:https://codesandbox.io/s/agitated-meadow-53w0u?file=/src/App.tsx

App.tsx

import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { useTypedSelector } from "./index";


function AddToDo() {
  const [input, setInput] = useState("");
  const dispatch = useDispatch();

  function handleInput(e: React.ChangeEvent<HTMLInputElement>) {
    setInput(e.target.value);
  }

  //dispatch to store
  function handleAddTodo() {
    dispatch({ text: input })
    setInput("");
  }

  return (
    <div>
      <input type="text" onChange={e => handleInput(e)} value={input} />
      <button type="button" onClick={handleAddTodo}>
        Add todo
      </button>
    </div>
  );
}

//TodoList
export interface Todo {
  text: string;
}

function TodoList() {
  const todos = useTypedSelector((state) => state)

  return (
    <ul className="todo-list">
      {todos.map((todo: Todo) => {
        return <Todo todo={todo} />;
      })}
    </ul>
  );
}

//Todo
function Todo({ todo }: { todo: Todo }) {
  return <li>{todo.text}</li>;
}

function App() {
  return (
    <div className="App">
      <AddToDo />
      <TodoList />
    </div>
  );
}

export default App;

index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider, TypedUseSelectorHook, useSelector } from 'react-redux'
import { createStore, combineReducers } from 'redux'
import App, { Todo } from './App';

//actions
const ADD_TODO = 'ADD_TODO'

type Action = AddTodo

export function addTodo(text: string) {
    return {
        type: ADD_TODO,
        text
    }
}
type AddTodo = ReturnType<typeof addTodo>

const INITIAL_STATE = [] as Todo[]
//reducer
function todoReducer(state = INITIAL_STATE, action: Action): Todo[] {
    switch (action.type) {
        case ADD_TODO:
            const todos: Todo[] = [
                ...state,
                {
                    text: action.text,
                }
            ]
            return todos
        default:

            return state
    }
}

const todoApp = combineReducers({
    todos: todoReducer
}
)

type RootState = ReturnType<typeof todoReducer>

export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector

//store
const store = createStore(todoApp)

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,

    document.getElementById('root')
);

【问题讨论】:

  • 为什么不在这里发布您的代码?
  • 我认为 todos 不是数组。
  • 请给minimal reproducible example。您可能在编译时键入了 Todo[],但运行时的 值是什么?
  • 您的代码框链接无效。您的 index.tsx 文件似乎不是一个好文件(它只是 app.tsx 的副本),而且,@types/redux 作为依赖项丢失了。
  • 错误提示todos不是一个数组,当你console.log(todos)..时你得到什么?

标签: reactjs typescript redux react-redux


【解决方案1】:

根据@JohnHass

因为 RootState 不是根状态的实际类型,所以您已经创建了 它是 Todo[] 当状态实际上是 { todos: Todo[] }


那么你需要改变的是从这个

const todos = useTypedSelector(state => state);

收件人:

const todos = useTypedSelector(state => state.todos);

状态不是返回数组而是带有 todos 键的对象,


另一个问题:

未传递事件类型:

function handleAddTodo() {
    dispatch({ text: input }); // <--- Not even passing action type
    setInput("");
}

改成:

function handleAddTodo() {
    dispatch({ type: "ADD_TODO", text: input });
    setInput("");
}

//-------------- OR ---------------

import { useTypedSelector , addTodo } from "./index";

//dispatch to store
function handleAddTodo() {
    dispatch(addTodo(input));
    setInput("");
}

【讨论】:

  • 我看到它适用于代码沙盒,但它不适用于我的机器:TypeScript error in /Downloads/53w0u/src/App.tsx(35,49): Property 'todos' does not exist on type 'Todo[]'. TS2339。我应该在哪里查看或提供哪些信息?
  • 注意你可以dispatch(addTodo(input));,动作创建者已经存在于索引中。
  • @JohnHass 因为RootState 不是根状态的实际类型,当状态实际上是{ todos: Todo[] } 时,您已将其设为Todo[]
  • @jonrsharpe 这解决了我的问题。谢谢。出于好奇,在沙盒中,为什么Todo[] 仍然有效?
  • @JohnHass,在线编辑工具可能存在一些限制,您能接受吗?
【解决方案2】:

问题

您的state.todos 是一个数组,但您将const todos 分配给整个state,即对象{ todos: Todo[], ... }

可能的解决方案

1。获取todos,仅在回调中请求该属性:

const todos = useTypedSelector((state) => state.todos)

2。解构赋值:

const { todos } = useTypedSelector((state) => state)

【讨论】:

  • 只是把它变成一个数组或者迭代它似乎是一个非常幼稚的解决方案;弄清楚 why 它是一个对象而不是 OP 所期望的数组,并修复它可能不是更好吗? 是什么对象呢?根据另一个答案,查看该值会非常清楚问题所在。
  • 我将他/她的问题误解为试图将可迭代对象与 map 一起使用。我将编辑我的答案,感谢您的反馈。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-29
  • 2020-06-17
  • 1970-01-01
  • 1970-01-01
  • 2020-03-03
  • 2021-06-05
相关资源
最近更新 更多