Home > Mobile >  TypeError: state is not iterable
TypeError: state is not iterable

Time:11-15

I am new to React js, and I am trying to create a contact list. Where you can add contacts, update, and delete contacts from your contact list. However, I am trying to initialize the state of my contact list but when I run it, it displays the contact list screen without the "initialState" of the contact list. Then when I try to add a contact it then throws a type error screen(state is not iterable). How do I fix this issue?

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);

CodePudding user response:

The initial state's an object with a contactReducer property that is the array of contacts.

I'm certain you've just specified incorrect default state value.

Instead of

state = { contactReducer: initialState }

You likely want

state = initialState

All the reducers will now correctly specify/update state as an array.

Since the contactReducer reducer was combined with another reducer and provided to the store, this nests the state slice under the key you combined them with.

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

Instead of accessing via just state.X it is now state.contactInfo.X.

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

CodePudding user response:

However, there might be an issue in delete contact case in the use of filter. Callback passed to a filter should return true or false only based on which it will be included or not in resultant array, instead you seem to have returned null or object.

Corrected:

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

The above filter will return true if the id's are not equal, which means it will be considered or false which means it will not be included in the resultant array

Additionally, in case of Update, you need to replace filter with map function as below:

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

Furthermore, i would changes the contactReducer.js as below:

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;
}
};
  • Related