How to use selectById selector generated from createEntityAdapter
Asked Answered
S

3

5

All examples on redux-toolkit website show usage of either selectIds or selectAll. Using either of them is simple. I have a redux-slice from where I am exporting

export const {
  selectById: selectUserById,
  selectIds: selectUserIds,
  selectEntities: selectUserEntities,
  selectAll: selectAllUsers,
  selectTotal: selectTotalUsers
} = usersAdapter.getSelectors(state => state.users)

then I am importing the selectors in my components and using like

const valueIAmInterestedIn = useSelector(selectUserIds)

I am interested in the code related to the usage of selectUserById.

Silvanus answered 12/6, 2020 at 0:50 Comment(0)
G
16

According to the documentation the by id selector has the following signature: selectById: (state: V, id: EntityId) => T | undefined.

So you can call it in your component in the following way:

const Component = ({ id }) => {
  const item = useSelector((state) =>
    selectUserById(state, id)
  );
};

This implementation of "normalization" may not work if you sort/filter entities on the server because the state would look more like:

{
  data: {
    entityType: {
      //query is key and value is status and result
      //  of the api request
      'sort=name&page=1': {
        loading: false,
        requested: true,
        error: false,
        stale: false,
        ids: [1, 2],
      },
      'sort=name:desc&page=1': {
        //would have loading, requested ....
        ids: [2, 1],
      },
      data: {
        //all the data (fetched so far)
        '1': { id: 1 },
        '2': { id: 2 },
      },
    },
  },
};

I have not worked with the "helpers" so have to look into it as it may facilitate for server side filtering and sorting.

I also doubt it will memoize the selector:

const List = ({ items }) => (
  <ul>
    {items.map(({ id }) => (
      <Item key={id} id={id} />
    ))}
  </ul>
);
const Item = React.memo(function Item({ id }) {
  //selectUserById is called with different id during
  // a render and nothing will be memoized
  const item = useSelector((state) =>
    selectUserById(state, id)
  );
});

I have created a short documentation on how you can use selectors created with reselect.

Genuine answered 12/6, 2020 at 8:27 Comment(0)
K
3

Selector can be created on top of selectById as below:

export const getLanguageById = (entityId: number) => {
  return createSelector(selectLanguageState, (state) =>
    selectById(state, entityId)
  );
};

This can be used in your component as below:

const language = useSelector(languageSelectors.getLanguageById(18));

I guess it's more straightforward and easy to read/ understand.

Thanks, Manish

Kolomna answered 9/8, 2021 at 17:17 Comment(1)
This is exactly what i was looking for when using multiple entity adapter in one reducer! Thanks a lots!Vuong
D
1

For what its worth, in 2024, this is a solution I went with. I hope this helps someone in the future

// entity.model.ts

type Entity = {
  id:number
  name: string
}
// entity.slice.ts

export const myEntityAdapter = createEntityAdapter({
  selectId: (entity: Entity) => entity.id,
  sortComparer: (a: Entity, b: Entity) => a.id - b.id,
});

export const entitySlice = createSlice({
  name: 'myEntitySlice ',
  initialState: myEntityAdapter.getInitialState(),
  reducers: {
   //...removed for brevity
  }
})

// entity-slice.selector.ts

const entityAdapterSlice = (state) => state.myEntitySlice;

export const { selectById } = myEntityAdapter.getSelectors(entityAdapterSlice);

export const getMyEntityById = createSelector(
  state => state,
  state => (entityId:number) => selectById(state, entityId)
);
// selection.selector.ts
export const listWithEntities = createSelector(
  [getListOfSelectedIds, getMyEntityById],
  (listOfSelectedIds: number[], getMyEntityById:(id:number) => Entity) => {
    return {
      listOfSelectedEntities: listOfSelectedIds.map(id => getMyEntityById(id))
    };
  }
);
// component.tsx

const selectedEntities = useSelector(listWithEntities)

Disembarrass answered 2/8 at 13:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.