Search code examples
reactjsreduxredux-toolkit

How do I implement my own selectedId for redux-toolkit?


I'm using redux to fetch some data using this api "https://valorant-api.com/v1/agents". The problem is I cannot show all the agents and only 1 showing up and I got warning from redux says:

the entity passed from selectId returned undefined, you should probably implement your own selectId function

warning from redux

I think there is mistake in my valorantSlice.

I try to show the list of agents like this

(in this api I want to show the details too)

export const fetchValorants = createAsyncThunk(
  "valorants/fetchValorants",
  async () => {
    const response = await axios
      .get("https://valorant-api.com/v1/agents")
      .then((response) => response.data.data)
      .then((agents) => {
        return Promise.all(
          agents.map((agent: any) =>
            axios.get(`https://valorant-api.com/v1/agents/${agent.uuid}`)
          )
        );
      })
      .then((agentDetails) => {
        return agentDetails.map((response) => response.data.data);
      });

    return response;
  }
);

Here is my slice

const valorantAdapter = createEntityAdapter();

const initialState = valorantAdapter.getInitialState({
  status: "idle",
  error: "" as any,
});

export const fetchValorants = createAsyncThunk(
  "valorants/fetchValorants",
  async () => {
    const response = await axios
      .get("https://valorant-api.com/v1/agents")
      .then((response) => response.data.data)
      .then((agents) => {
        return Promise.all(
          agents.map((agent: any) =>
            axios.get(`https://valorant-api.com/v1/agents/${agent.uuid}`)
          )
        );
      })
      .then((agentDetails) => {
        return agentDetails.map((response) => response.data.data);
      });

    return response;
  }
);

const valorantsSlice = createSlice({
  name: "valorants",
  initialState,
  reducers: {},
  extraReducers(builder) {
    builder
      .addCase(fetchValorants.pending, (state, action) => {
        state.status = "loading";
      })
      .addCase(fetchValorants.fulfilled, (state, action) => {
        state.status = "succeeded";
        action.payload.forEach((agent) => {
          valorantAdapter.addOne(state, agent);
        });
      })
      .addCase(fetchValorants.rejected, (state, action) => {
        state.status = "failed";
        state.error = action.error.message;
      });
  },
});

export default valorantsSlice.reducer;
export const {
  selectAll: selectAllValorants,
  selectById: selectValorantByUuid,
  selectIds: selectValorantUuids,
} = valorantAdapter.getSelectors((state: any) => state.valorants) as any;

I use react-redux in nextjs 13

redux devtools screenshot redux devtools screenshot


Solution

  • The undefined under the entity key make me think response from Valorant API don't possess any ID. (EDIT : with additional screenshot, theory seems validated :))

    By default the entityAdapter of RTK will look for an id.

    Based on the code of the thunk it seems that there is an uuid instead. https://redux-toolkit.js.org/api/createEntityAdapter#selectid

    So you can pass a selectId to createEntityAdapter which will target the uuid key.

    const valorantAdapter = createEntityAdapter({
      selectId: (valorant) => valorant.uuid,
    })