TypeError: state is not iterable

TypeError: state is not iterable

我是 React js 的新手,我正在尝试创建联系人列表。您可以在其中添加联系人、更新联系人以及从联系人列表中删除联系人。但是,我正在尝试初始化我的联系人列表的状态,但是当我 运行 它时,它显示的联系人列表屏幕没有联系人列表的“initialState”。然后,当我尝试添加联系人时,它会抛出一个类型错误屏幕(状态不可迭代)。我该如何解决这个问题?

contactReducer.js:

const initialState = [
    { id: 0, name: "Raman Sharma", Level:  " 20", Progress: "Master" },
    { id: 1, name: "Test Name", Level:  " 25", Progress: "Master" },
  ];
  
  export const contactReducer = (state = initialState , action) => {
    switch (action.type) {
      case "ADD_CONTACT":
        state = [...state, action.payload];
        return state;
      case "DELETE_CONTACT":
        const contactFilter = state.filter((contact) =>
          contact.id === action.payload ? null : contact
        );
        state = contactFilter;
        return state;
      case "UPDATE_CONTACT":
        const contactUpdate = state.filter((contact) =>
          contact.id === action.payload.id
            ? Object.assign(contact, action.payload)
            : contact
        );
        state = contactUpdate;
        return state;
      case "RESET_CONTACT":
        state = [{ name: null, Level: null, Progress: null }];
        return state;
      default:
        return state;
    }
  };
  

./client/AddContact/index.js

import React, { useState } from "react";
import { connect } from "react-redux";
import { useHistory } from "react-router";
import { toast } from "react-toastify";

const AddPost = ({ contacts, addContact }) => {
  const [name, setName] = useState("");
  // const [Level, setLevel] = useState("");
  // const [Progress, setProgress] = useState("");

  const history = useHistory();

  const handleSubmit = (e) => {
    e.preventDefault();
    // const checkContactLevelExists = contacts.filter((contact) =>
    //   contact.Level === Level ? contact : null
    //);
    // const checkContactProgressExists = contacts.filter((contact) =>
    //   contact.Progress === Progress ? contact : null
    //);

    if ( !name) {
      return toast.warning("Please fill in field!!");
    }
    // if (checkContactLevelExists.length > 0) {
    //   return toast.error("This Level already exists!!");
    // }
    // if (checkContactProgressExists.length > 0) {
    //   return toast.error("This Progress number already exists!!");
    // }

    const data = {
      id: contacts.length > 0 ? contacts[contacts.length - 1].id + 1 : 0,
      // Level,
      name,
      // Progress,
    };

    addContact(data);
    toast.success("Player Added successfully!!");
    history.push("/");
  };

  return (
    <div className="container-fluid">
      <h1 className="text-center text-dark py-3 display-2">Add Friend</h1>
      <div className="row">
        <div className="col-md-6 p-5 mx-auto shadow">
          <form onSubmit={handleSubmit}>
            <div className="form-group">
              <input
                className="form-control"
                type="text"
                placeholder="Full name"
                value={name}
                onChange={(e) => setName(e.target.value)}
              />
            </div>

            {/* <div className="form-group">
              <input
                className="form-control"
                type="Level"
                placeholder="Level"
                value={Level}
                onChange={(e) => setLevel(e.target.value)}
              />
            </div> */}
            
            {/* <div className="form-group">
              <input
                className="form-control"
                type="number"
                placeholder="Progress"
                value={Progress}
                onChange={(e) => setProgress(e.target.value)}
              />
            </div> */}
            <div className="form-group">
              <input
                className="btn btn-block btn-dark"
                type="submit"
                value="Add Student"
              />
            </div>
          </form>
        </div>
      </div>
    </div>
  );
};

const mapStateToProps = (state) => ({
  contacts: state,
});
const mapDispatchToProps = (dispatch) => ({
  addContact: (data) => {
    dispatch({ type: "ADD_CONTACT", payload: data });
  },
});

export default connect(mapStateToProps, mapDispatchToProps)(AddPost);

home.js

import React from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";

const Friends = ({ contacts, deleteContact }) => {
  return (
    <div className="container">
      <div className="row d-flex flex-column">
        <Link to="/add" className="btn btn-outline-dark my-5 ml-auto ">
          Add Friend
        </Link>
        <div className="col-md-10 mx-auto my-4">
          <table className="table table-hover">
            <thead className="table-header bg-dark text-white">
              <tr>
                <th scope="col">Id</th>
                <th scope="col">Name</th>
                <th scope="col">Level</th>
                <th scope="col">Progess</th>
                <th scope="col">Actions</th>
              </tr>
            </thead>
            <tbody>
              {contacts.length > 0 ? (
                contacts.map((contact, id) => (
                  <tr key={id}>
                    <td>{id + 1}</td>
                    <td>{contact.name}</td>
                    <td>{contact.Level}</td>
                    <td>{contact.Progress}</td>
                    <td>
                      <Link
                        to={`/edit/${contact.id}`}
                        className="btn btn-sm btn-primary mr-1"
                      >
                        Edit
                      </Link>
                      <button
                        type="button"
                        onClick={() => deleteContact(contact.id)}
                        className="btn btn-sm btn-danger"
                      >
                        Remove
                      </button>
                    </td>
                  </tr>
                ))
              ) : (
                <tr>
                  <th>No contacts found</th>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
};

const mapStateToProps = (state) => ({
  contacts: state,
});

const mapDispatchToProps = (dispatch) => ({
  deleteContact: (id) => {
    dispatch({ type: "DELETE_CONTACT", payload: id });
  },
});

export default connect(mapStateToProps, mapDispatchToProps)(Friends);

初始状态是一个具有 contactReducer 属性 的对象,即联系人数组。

我确定您刚刚指定了错误的默认状态值。

而不是

state = { contactReducer: initialState }

你可能想要

state = initialState

现在所有的 reducer 都会正确地 specify/update 状态为数组。

由于 contactReducer reducer 与另一个 reducer 组合并提供给商店,这将状态切片嵌套在您将它们组合在一起的键下。

const reducer = combineReducers({
  login: authReducer,
  contactInfo: contactReducer, // <-- this
});

现在是 state.contactInfo.X.

,而不是仅通过 state.X 访问
const mapStateToProps = (state) => ({
  contacts: state.contactInfo,
});

但是,在使用过滤器时删除联系人案例可能会出现问题。 传递给过滤器的回调应该 return true 或 false 仅基于它将包含或不包含在结果数组中,而不是你似乎 returned null 或 object.

更正:

case "DELETE_CONTACT":
    const contactFilter = state.filter((contact) =>
      contact.id !== action.payload
    );
    state = contactFilter;
    return state;

如果 id 不相等,上面的过滤器将 return 为真,这意味着它会被考虑或为假,这意味着它不会被包含在结果数组中

另外,如果是Update,你需要用map函数替换filter,如下:

case "UPDATE_CONTACT":
    const contactUpdate = state.map((contact) =>
      contact.id === action.payload.id
        ? {...contact, ...action.payload}
        : contact
    );
    state = contactUpdate;
    return state;

此外,我将 contactReducer.js 更改如下:

const initialState = { contacts: [
{ id: 0, name: "Raman Sharma", Level:  " 20", Progress: "Master" },
{ id: 1, name: "Test Name", Level:  " 25", Progress: "Master" },
]};
export const contactReducer = (state = initialState , action) => {
switch (action.type) {
  case "ADD_CONTACT":
    return {contacts: [...state.contacts, action.payload]};
  case "DELETE_CONTACT":
    return {contacts: state.contacts.filter((contact) =>
      contact.id !== action.payload
    )};
  case "UPDATE_CONTACT":
    return {contacts: state.contacts.map((contact) =>
      contact.id === action.payload.id
        ? {...contact, ...action.payload}
        : contact
    )};
  case "RESET_CONTACT":
    return {contacts: [{ name: null, Level: null, Progress: null }]};
  default:
    return state;
}
};