Search code examples
javascriptreactjsreact-reduxredux-toolkit

Updating a specific object data in my rtk asyncthunk


I am new to rtk and immer. I have a problem regarding updating my state with my redux. Lets say when I logging in my user, I will store my user's data in redux with these kind of structure:

data = {
    "user_profile" : {
    "name": "John Doe",
    "Token": "12345678"
  },
  "basic_information": {
    "primary_information": {
        "sex": "MALE",
        "age": 30,
        "photo": "test.png"
    }
  }
}

now when i will call my update api, then my api returns something like these updated values:

updated_data = {
    "user_profile" : {
    "name": "John Test",   
  },
  "basic_information": {
    "primary_information": {
        "sex": "FEMALE",
        "age": 27,
    }
  }
}

I want to update my current state based on my api update state values, but i want the token will remain and other data that i didnt update.

Here is what i did/tried with the asyncthunk rtk. I have a slice:

export const updatePrimaryInformation = createAsyncThunk(
  "profile/primary_info",
  async (arg, { rejectWithValue }) => {
    const { body, headers } = arg;
    try {
      const updateRequest = await commonPutRequest({
        body,
        headers,
        url: "http://localhost:8000/api/accounts_app/primary_information",
      });
      if (updateRequest) {
        return updateRequest;
      } else {
        throw new Error(updateRequest.message);
      }
    } catch (err) {
      return rejectWithValue(err.message);
    }
  }
);

// loading: 'idle' | 'pending' | 'succeeded' | 'failed'
    const initialState = {
      data: {},
      loading: "idle",
      message: "",
    };


const authenticateSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      //login
      .addCase(fetchUserLogin.pending, (state) => {
        state.loading = "pending";
      })
      .addCase(fetchUserLogin.fulfilled, (state, action) => {
        state.loading = "succeeded";
        state.data = action.payload;
      })
      .addCase(fetchUserLogin.rejected, (state, action) => {
        state.loading = "failed";
        state.message = action.payload;
      })
      //logout
      .addCase(logoutUser.pending, (state) => {
        state.loading = "pending";
      })
      .addCase(logoutUser.fulfilled, (state, action) => {
        return initialState;
      })
      .addCase(logoutUser.rejected, (state, action) => {
        state.loading = "failed";
        state.message = action.payload;
      })
      //updatePrimaryInfo
      .addCase(updatePrimaryInformation.pending, (state) => {
        state.loading = "pending";
      })
      .addCase(updatePrimaryInformation.fulfilled, (state, action) => {
        state.loading = "succeeded";        
        state.data.basic_information.primary_information = action.payload.basic_information.primary_information;        
      })
      .addCase(updatePrimaryInformation.rejected, (state, action) => {
        state.loading = "failed";
        // state.message = action.payload;
      });
    // .addCase(userLogout, (state) => {
    //   return initialState;
    // });
  },
});

Solution

  • Redux or immer doesn't have this functionality out of the box.

    It sounds like you want to do the merge operation from lodash. This merges two objects together by only inserting undefined keys.

    You can install lodash or look at the function and reimplment it. Lodash also has partial imports.

          .addCase(fetchUserLogin.fulfilled, (state, action) => {
            state.loading = "succeeded";
            state.data = _.merge(action.payload, state.data);
          })