1

I'm working on implementing beautiful-dnd in my react project.

I have the following data:

const initialData = {
        users: {
            'user-1': { id: 'user-1', name: 'John'},
            'user-2': { id: 'user-2', name: 'Patrick'},
            'user-3': { id: 'user-3', name: 'Malorie'},
            'user-4': { id: 'user-4', name: 'Eric'},
            'user-5': { id: 'user-5', name: 'Bob'},
            'user-6': { id: 'user-6', name: 'Blob'}
        },
        areas: {
            'area-0': {
                id: 'area-0',
                title: 'Main Area',
                userIds: ['user-1', 'user-2', 'user-3', 'user-4', 'user-5', 'user-6']
            },
            'area-1': {
                id: 'area-1',
                title: 'Area 1',
                userIds: []
            },
            'area-2': {
                id: 'area-2',
                title: 'Area 2',
                userIds: []
            }
        },
        areaOrder: ['area-0', 'area-1', 'area-2'],
    }

In the reducer, I try to remove one of the users this way:

case REMOVE_USER_ACTION:
    return {
        ...state,
        users: [ ...state.users.filter(user => user !== action.id) ]
    }

I'm getting this error:

TypeError: e.users.filter is not a function or its return value is not iterable

I searched for this error, but I'm unable to find a comparable scenario and how to fix this issue.

Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
DouzeBri DouzeBra
  • 117
  • 1
  • 2
  • 13

3 Answers3

2

TypeError: e.users.filter is not a function or its return value is not iterable

It means that users is not an array, it's object instead.

So you can get the filtered users in this way:

const users = {'user-1':{id:'user-1',name:'John'},'user-2':{id:'user-2',name:'Patrick'},'user-3':{id:'user-3',name:'Malorie'},'user-4':{id:'user-4',name:'Eric'},'user-5':{id:'user-5',name:'Bob'},'user-6':{id:'user-6',name:'Blob'}};

const actionId = "user-1";
const filteredUsers = Object.entries(users).filter(([key, value]) => value.id != actionId);
console.log(Object.fromEntries(filteredUsers));

As a result, your REMOVE_USER_ACTION looks like

case REMOVE_USER_ACTION:
    return {
        ...state,
        users: filteredUsers
    }

However, you should change users type from object to array in terms of performance, meaningful name & clean code.

const users = [{ id: 'user-1', name: 'John'},
         { id: 'user-2', name: 'Patrick'},
         { id: 'user-3', name: 'Malorie'},
         { id: 'user-4', name: 'Eric'},
         { id: 'user-5', name: 'Bob'},
         { id: 'user-6', name: 'Blob'}
       ];
       
const actionId = "user-1";
const filteredUsers = users.filter(item => item.id != actionId);
console.log(filteredUsers);
Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
  • I disagree that it should be an array. A keyed object makes lookup operations much easier. – Linda Paiste Apr 09 '21 at 22:14
  • Agree. To be more precise. *If you want to supply `specific keys`, the only choice is an `object`. If you `don't care about the keys, an array it is.* [Array vs. Object efficiency in JavaScript](https://stackoverflow.com/questions/17295056/array-vs-object-efficiency-in-javascript) @LindaPaiste – Nguyễn Văn Phong Apr 10 '21 at 02:29
1

Can't filter the state.users if your data is an object. It's either you change the initialData shape or if it's really like that, then you'd have to do some way to delete a key from your object.

case REMOVE_USER_ACTION:
    const users = { ...state.users };
    delete users[action.id];
    return {
        ...state,
        users,
    }
Leomar Amiel
  • 469
  • 3
  • 13
1

users is an object, not an array, so you can't iterate over it

Here is a possible solution using Object.entries

case REMOVE_USER_ACTION:
  return {
    ...state,
    users: Object.entries(state.users).reduce((acc, [key, value]) => {
      if (key !== action.id) {
        acc[key] = value;
      }
      return acc;
    }, {})
  }
Olivier Boissé
  • 15,834
  • 6
  • 38
  • 56