Search code examples
reactjsreact-reduxredux-toolkitrtk-query

Combine RTK query and redux for a muation scenario


I have created an api slice like this:

export const authSlice = createApi({
    reducerPath: "auth",
    baseQuery: fetchBaseQuery({
        baseUrl: resolveApiBaseUrl(),
        prepareHeaders(headers) {
            headers.set("Content-Type", "application/json")
            headers.set("X-XSRF-TOKEN", <string>resolveUserToken())
            return headers
        }
    }),

    endpoints(builder) {
        return {
            login: builder.mutation<GymAppResponse, Login>({
                query: (payload) => ({
                    url: "/login",
                    method: "POST",
                    body: payload
                }),
            })
       }
})

export const {
    useLoginMutation,
} = authSlice

I am getting the user token after login action and store it in localStorage so I can limit user access to some part of my application.

  1. I am trying to use redux toolkit in order to interact with userToken and as default state for userToken I want to use localStorage value. So I dont want to write code in my component to check the localStorage and do the necessary logic. I want to use redux so when the userToken value updated in localStorage, Redux will automatically update the component for me using useEffect(() => {...}, userTokenWhichRetrivedFromRedux).
  2. I couldnt find any information on the doc for how to use mutation and combine it with redux toolkit! I used to use redux-saga which I called redux actions after a success or fail request/response.

Question: so I am asking all the knowledgeable fellows out there:

  1. What is the best practice here?
  2. Unfortunately redux-persist is not being actively maintained and I have to use localStorage prgramatically. Is there a package or best practice for handling data which we need to persist after an Api call ?

Solution

  • Here is how I did it in a similar use case :

    export const authSlice = createSlice({
       name: "auth",
       initialState,
       reducers: {},
       extraReducers: (builder) => {
         builder
           .addMatcher(authApi.endpoints.login.matchFulfilled, (state, action) => {
             state.user = action.payload;
           })
           .addMatcher(authApi.endpoints.logout.matchFulfilled, (state, action) => {
            state.user = null;
           });
       },
    });
    

    You can also use the createListenerMiddleware to access a state slice and provide a callback that will execute based on matchers

    const authListenerMiddleware = createListenerMiddleware();
    
    authListenerMiddleware.startListening({
      matcher: isAnyOf(
        authApi.endpoints.login.matchFulfilled,
        authApi.endpoints.logout.matchFulfilled
      ),
      effect: (action, listenerApi) => {
        if (action.meta.arg.endpointName === "login") {
          const user = listenerApi.getState().auth.user;
          localStorage.setItem(AUTHENTICATED_USER, JSON.stringify(user));
        }
        if (action.meta.arg.endpointName === "logout") {
          localStorage.removeItem(AUTHENTICATED_USER);
        }
      },
    });