skip to Main Content

I want to show the data of customers from Redux and the data is successfully stored in Redux. I checked it with ReactDevTools the error is when I use useSelect to retrieve the data from the store.

error:

TypeError: entries.map is not a function
    at Entries (http://localhost:5173/src/pages/Entries.jsx?t=1718705214409:80:59)
    at renderWithHooks (http://localhost:5173/node_modules/.vite/deps/chunk-QO4NA2F3.js?v=1f9f66b9:11568:26)
    at mountIndeterminateComponent (http://localhost:5173/node_modules/.vite/deps/chunk-QO4NA2F3.js?v=1f9f66b9:14946:21)
    at beginWork (http://localhost:5173/node_modules/.vite/deps/chunk-QO4NA2F3.js?v=1f9f66b9:15934:22)
    at beginWork$1 (http://localhost:5173/node_modules/.vite/deps/chunk-QO4NA2F3.js?v=1f9f66b9:19781:22)
    at performUnitOfWork (http://localhost:5173/node_modules/.vite/deps/chunk-QO4NA2F3.js?v=1f9f66b9:19226:20)
    at workLoopSync (http://localhost:5173/node_modules/.vite/deps/chunk-QO4NA2F3.js?v=1f9f66b9:19165:13)
    at renderRootSync (http://localhost:5173/node_modules/.vite/deps/chunk-QO4NA2F3.js?v=1f9f66b9:19144:15)
    at recoverFromConcurrentError (http://localhost:5173/node_modules/.vite/deps/chunk-QO4NA2F3.js?v=1f9f66b9:18764:28)
    at performConcurrentWorkOnRoot (http://localhost:5173/node_modules/.vite/deps/chunk-QO4NA2F3.js?v=1f9f66b9:18712:30)

App.jsx where all the data is dispatched to store

useEffect(() => {
  const initializeApp = async () => {
    try {
      const userData = await authService.getCurrentUser();
      if (userData) {
        dispatch(login({ userData }));
      } else {
        dispatch(logout());
      }

      const entries = await service.getEntries();
      if (entries ) {
        dispatch(setEntries(entries));
      }
    } catch (error) {
      console.error('Initialization error:', error);
    } finally {
      setLoading(false);
    }
  };

  initializeApp();
}, [dispatch]);

Entries.jsx Where I am trying to show the data

import React from 'react';
import { useSelector } from 'react-redux';
import { Link } from 'react-router-dom';

function Entries() {
  const entries = useSelector(state => state.entry.entries)
    
  console.log(entries);

  return (
    <div className='flex flex-col'>
      <div className='mb-4'>
        <Link className='bg-slate-600 text-white p-2 rounded' to='/addentry'>Add Entry</Link>
      </div>

      <div className="w-full">
        {entries.length === 0 ? (
          <div>No entries available</div>
        ) : (
          <table className="min-w-full bg-white">
            <thead>
              <tr>
                <th className="py-2">Customer Name</th>
                <th className="py-2">Document Name</th>
                <th className="py-2">Reception Date</th>
                <th className="py-2">Submitter's Name</th>
                <th className="py-2">Restitution Date</th>
                <th className="py-2">Collector's Name</th>
              </tr>
            </thead>
            <tbody>
              {entries.map((entry) => (
                <tr key={entry.id} className="text-center border-b">
                  <td className="py-2">{entry.customer_name}</td>
                  <td className="py-2">{entry.document_name}</td>
                  <td className="py-2">{entry.date}</td>
                  <td className="py-2">{entry.submitors_name}</td>
                  <td className="py-2">{entry.restitution_date }</td>
                  <td className="py-2">{entry.collectors_name}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

export default Entries;

service class:
(I have used proxy here therefor half URL and it’s working correctly)

import axios from 'axios';

export class Service {
  async createEntry({ docName, subName }) {
    try {
      const response = await axios.post('/api/v1/e', {
        docName,
        subName
      });
      return response.data;
    } catch (error) {
      console.log("Custom backend service :: createEntry :: error", error);
    }
  }

  async updateEntry({ docName, subName, entryid }) {
    try {
      const url = `/api/v1/e/${entryid}`;
      const response = await axios.put(url, {
        docName, subName
      });
      return response.data;
    } catch (error) {
      console.log("Custom backend service :: updateEntry :: error", error);
    }
  }
    
  async deleteEntry(entryid) {
    try {
      const url = `/api/v1/e/${entryid}`;
      await axios.delete(url);
      return true;
    } catch (error) {
      console.log("Custom backend service :: deleteEntry :: error", error);
      return false;
    }
  }
    
  async getEntries() {
    try {
      const response = await axios.get('api/v1/e');
      return response.data;
    } catch (error) {
      console.log("Custom backend service :: getEntries :: error", error);
      return false;
      }
  }

  async getEntry(entryid) {
    try {
      const response = await axios.get('/api/v1/e');
      return response.data.data;
    } catch (error) {
      console.log("Custom backend service :: getPost :: error", error);
      return false;
    }
  }
}

const service = new Service();
export default service;

entrySlice.js:

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  entries: [],
};

const entrySlice = createSlice({
  name: "entry",
  initialState,
  reducers: {
    setEntries: (state, action) => {
      state.entries = action.payload; 
    },
    addEntry: (state, action) => {
      state.entries.push(action.payload);
    },
  },
});

export const { setEntries, addEntry } = entrySlice.actions;

export default entrySlice.reducer;

Store.js:

import { configureStore}  from '@reduxjs/toolkit';
import authSlice from './slice/authSlice';
import custSlice from './slice/custSlice';
import entrySlice from './slice/entrySlice';

const store = configureStore({
  reducer: {
    auth: authSlice,
    entry: entrySlice,
    customer: custSlice,
  }
});

export default store;

data example from Post man

{
    "statusCode": 200,
    "data": [
        {
            "id": 1,
            "customer_id": null,
            "employe_id": null,
            "document_name": "Pan",
            "reception_date": "2024-03-04T18:30:00.000Z",
            "submitors_name": "ved bhoskar",
            "restitution_date": null,
            "collectors_name": null
        },
        {
            "id": 2,
            "customer_id": null,
            "employe_id": null,
            "document_name": "Pan",
            "reception_date": "2024-03-04T18:30:00.000Z",
            "submitors_name": "ved bhoskar",
            "restitution_date": null,
            "collectors_name": null
        },
    ],
    "message": "Entries retrieved successfully",
    "success": true
}

console logging entries

2

Answers


  1. You’re trying to map over an object, that won’t work.

    You’ll need entries.data.map since you’re pasting the complete object in your store, but the array you want to map over is in data.

    Also consider updating the if:

    {entries?.data.length === 0 ? (
        <div>No entries available</div>
    ) : (
        entries.data.map()
    )}
    
    Login or Signup to reply.
  2. Issue

    You are storing the entire entries response into your store, which is an object and thus doesn’t have length or map properties to use in the UI.

    Solution Suggestion

    While you could access the entries.data array of the object in the UI like the other answer suggests it would make a bit more sense to only store the exact data you need.

    Examples:

    dispatch(setEntries(entries.data));
    
    ...
    
    setEntries: (state, action) => {
      state.entries = action.payload; 
    },
    
    dispatch(setEntries(entries));
    
    ...
    
    setEntries: (state, action) => {
      state.entries = action.payload.data; 
    },
    

    Or to select the exact state you want in the UI if you are storing the entire response object in the store.

    Example:

    const entries = useSelector(state => state.entry.entries.data);
    

    From here entries will be the array you were expecting all along.

    <div className="w-full">
      {entries.length ? (
        <table className="min-w-full bg-white">
          <thead>
            <tr>
              <th className="py-2">Customer Name</th>
              <th className="py-2">Document Name</th>
              <th className="py-2">Reception Date</th>
              <th className="py-2">Submitter's Name</th>
              <th className="py-2">Restitution Date</th>
              <th className="py-2">Collector's Name</th>
            </tr>
          </thead>
          <tbody>
            {entries.map((entry) => (
              <tr key={entry.id} className="text-center border-b">
                <td className="py-2">{entry.customer_name}</td>
                <td className="py-2">{entry.document_name}</td>
                <td className="py-2">{entry.date}</td>
                <td className="py-2">{entry.submitors_name}</td>
                <td className="py-2">{entry.restitution_date }</td>
                <td className="py-2">{entry.collectors_name}</td>
              </tr>
            ))}
          </tbody>
        </table>
      ) : (
        <div>No entries available</div>
      )}
    </div>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search