Problems about appending new state to old state in redux thunk - reactjs

IMAGE LINK HERE. it doubles the initial return.
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit'
import axios from 'axios'
let url = 'http://localhost:3000/api/v1/posts'
const initialState = {
allPosts: [],
isLoading: false,
isError: false,
}
export const getAllPost = createAsyncThunk('allPosts/getAllPost', async (page, thunkAPI) => {
console.log(page)
try {
const { data } = await axios.get(`${url}?page=${page}`)
return data.posts
} catch (error) {
console.log(error)
}
})
const allPostSlice = createSlice({
name: 'allPosts',
initialState,
reducers: {},
extraReducers: {
[getAllPost.pending]: (state) => {
state.isLoading = true
},
[getAllPost.fulfilled]: (state, { payload }) => {
console.log(payload)
state.allPosts = [...state.allPosts, ...payload]
state.isLoading = false
},
[getAllPost.rejected]: (state) => {
state.isLoading = false
state.isError = true
},
},
})
export default allPostSlice.reducer
When I try to append ...state.allPosts, which is supposed to be the old state, it prints double the initial payload. I only need it printed once, not twice.
[getAllPost.fulfilled]: (state, { payload }) => {
console.log(payload)
state.allPosts = [...state.allPosts, ...payload]
state.isLoading = false
},
How can I print my payload only once using state.allPosts?

Related

Splitting the redux state where some variables are common

I'm using redux-toolkit for my chat application. Currently there are two slices in the store:
userSlice - for managing the user state
import { createSlice } from "#reduxjs/toolkit";
import appApi from "../services/appApi";
const endpoints = [
appApi.endpoints.signUpUser,
appApi.endpoints.loginUser,
appApi.endpoints.profileUser,
appApi.endpoints.logout,
];
export const userSlice = createSlice({
name: "user",
initialState: {
loading: false,
error: null,
_id: null,
},
reducers: {
addNotifications: (state, { payload }) => {},
resetNotifications: (state, { payload }) => {},
clearError: (state) => {
state.error = null;
state.loading = false;
},
setError: (state,{payload}) =>
{
state.error = payload
},
loadID: (state) => {
state._id =
JSON.parse(localStorage.getItem("cherry-chat-status")) || null;
},
setLoading: (state, { payload }) =>
{
state.loading = payload
}
},
extraReducers: (builder) => {
builder.addMatcher(
appApi.endpoints.signUpUser.matchFulfilled,
(state, { payload }) => {
localStorage.setItem("cherry-chat-status", JSON.stringify(payload._id));
return { ...state, loading: false, error: null, ...payload };
}
);
builder.addMatcher(
appApi.endpoints.loginUser.matchFulfilled,
(state, { payload }) => {
localStorage.setItem("cherry-chat-status", JSON.stringify(payload._id));
return { ...state, loading: false, error: null, ...payload };
}
);
builder.addMatcher(
appApi.endpoints.profileUser.matchFulfilled,
(state, { payload }) =>
{
return { ...state, loading: false, error: null, ...payload };
}
);
builder.addMatcher(
appApi.endpoints.profileUser.matchFulfilled,
(state, { payload }) => {
return { ...state, loading: false, error: null, ...payload };
}
);
builder.addMatcher(appApi.endpoints.logout.matchFulfilled, () => {
localStorage.removeItem("cherry-chat-status");
return { loading: false, error: null };
});
endpoints.forEach(({ matchPending, matchRejected }) => {
builder.addMatcher(matchPending, (state) => {
state.loading = true;
state.error = null;
});
builder.addMatcher(matchRejected, (state, { payload: error }) => {
state.error = error?.data?.message;
state.loading = false;
});
});
},
});
export const {
addNotifications,
resetNotifications,
clearError,
loadID,
setError,
setLoading,
} = userSlice.actions;
export default userSlice.reducer;
messageSlice - for managing the messages
import { createSlice } from "#reduxjs/toolkit";
import io from "socket.io-client";
import msgApi from "../services/msgApi";
const SOCKET_URL = "localhost:5000";
export const socket = io(SOCKET_URL);
const endpoints = [msgApi.endpoints.profileUserRooms];
export const messageSlice = createSlice({
name: "message",
initialState: {
rooms: [],
currentRoom: [],
members: [],
messages: [],
privateMemberMsg: {},
newMessages: {},
error: null,
},
reducers: {
addMembers: (state, { payload }) => {
state.members = payload;
},
},
extraReducers: (builder) => {
// your extra reducers go here
builder.addMatcher(
msgApi.endpoints.profileUserRooms.matchFulfilled,
(state, { payload }) => {
//I want set the loading state of the user Slice to be false
return {
...state,
rooms: payload,
};
}
);
endpoints.forEach(({ matchPending, matchRejected }) => {
builder.addMatcher(matchPending, (state) => {
state.error = null;
//I want set the loading state of the user Slice to be true
});
builder.addMatcher(matchRejected, (state, { payload: error }) => {
state.error = error?.data?.message;
//I want set the loading state of the user Slice to be false
});
});
},
});
export const { addMembers } = messageSlice.actions;
export default messageSlice.reducer;
Is there any method such that I can access the loading and error from the user slice and can be set at the messageSlice also? Or should I change the structure of the state or should I duplicate those variables in both slices?

Redux toolkit how to call action from other slice on one action fullfilled

I have a async action name editLoginIdData() in loginsIdSlice.js,
which i am dispatching from certain component, which edits the data in mongoDB database, then when the action is fullfilled, i mutate the state in extraReducers
editLoginIdData.fulfilled.
But now what i want to do that whenever the editLoginIdData action is fullfilled
i want to also add the updated(which i will get from server responese -> updatedData at editLoginIdData()) data to activitiesData state, which i am handling in activitiesSlice.js
So basically is there a way that when editLoginIdData action is fullfilled we can
dispatch somehow mutate the state in other slice.
One approach i have taken is to import the editLoginIdData() action in activitiesSlice.js and then creating a extraReducer with editLoginIdData.fullfilled
and mutating activitiesData state.
I have done the above approach and seems its working correctly.
But there is a catch, like how show i get the response data at editLoginIdData()
to passed to activitiesSlice.js because i will required that updated data.
if the above appraoch is not correct, then how should i do it
loginsIdSlice.js
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import * as api from "../../api"
const initialState = {
loginsIdData: [],
}
export const fecthLoginIdsData = createAsyncThunk("loginIds/fetch", async ({ user_id }, { getState }) => {
const res = await api.fetchUserLoginIds(user_id);
console.log(res);
const { data } = res;
data.reverse();
return data;
});
export const addNewLoginIdData = createAsyncThunk("loginIds/add", async ({ data, user_id }, { getState }) => {
const res = await api.addNewLoginIdA(data, user_id)
const { loginIdsArray } = res.data;
return loginIdsArray[loginIdsArray.length - 1];
});
export const editLoginIdData = createAsyncThunk("loginIds/edit", async ({ updatedData, login_id }, { getState }) => {
const res = await api.editLoginId(login_id, updatedData);
// console.log(updatedData);
return updatedData;
});
export const deleteLoginData = createAsyncThunk("loginIds/delete", async ({ login_id, user_id }, { getState }) => {
const res = await api.deleteLoginId(login_id, user_id);
// console.log(res);
const { data } = res;
// console.log(data);
return data.reverse();
});
//* Slice
const loginsIdSlice = createSlice({
name: 'loginsId',
initialState: initialState,
extraReducers: (builder) => {
builder.
addCase(fecthLoginIdsData.fulfilled, (state, action) => {
return {
...state,
loginsIdData: action.payload
};
}).
addCase(addNewLoginIdData.fulfilled, (state, action) => {
return {
...state,
loginsIdData: [action.payload, ...state.loginsIdData]
};
}).
addCase(editLoginIdData.fulfilled, (state, action) => {
const newArray = state.loginsIdData.map((loginId) => {
if (loginId._id === action.payload._id) {
return action.payload;
} else {
return loginId;
}
});
return {
...state,
loginsIdData: newArray,
};
}).
addCase(deleteLoginData.fulfilled, (state, action) => {
return {
...state,
loginsIdData: action.payload
};
})
}
})
export const { deleteLoginId, editLoginId } = loginsIdSlice.actions;
export default loginsIdSlice.reducer;
activitiesSlice
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import * as api from "../../api"
import { editLoginIdData } from "../loginsId/loginsIdSlice"
const initialState = {
activitiesData: [],
}
const activitiesSlice = createSlice({
name: 'activities',
initialState: initialState,
extraReducers: (builder) => {
builder.
addCase(editLoginIdData.fullfilled, (state, action) => {
console.log("ss")
return {
...state,
activitiesData: []
};
})
}
})
export default activitiesSlice.reducer;
Is there a way that when editLoginIdData action is fullfilled we can
dispatch somehow mutate the state in other slice.

Data fetched from server not loading into redux store?

I'm fetching data from a mongoDB database and then fetch that data from the server and finally render the data to the UI in a specified component. I'm using redux-toolkit for state management.
The problem is when fetching the data from the server it is not visible in the store. Why is the empty array in the initial state still empty after fetching the data? I'm using createSlice Api that generates the action creators and action types and createAsyncThunk Api for the asynchronous task of fetching the data from the server.
Slice reducer
import { createSlice, createAsyncThunk} from '#reduxjs/toolkit'
import axios from 'axios'
const initialState = {
realestate: [],
isSuccess: false,
isLoading: false,
message: '',
}
export const getRealEstates = createAsyncThunk(
'realestate/getRealEstates', async () => {
try {
const response = await axios.get('castles')
return response.data
} catch (error) {
console.log(error)
}
}
)
export const estateSlice = createSlice({
name: 'realestate',
initialState,
reducers: {
reset: (state) => initialState,
},
extrareducers: (builder) => {
builder.addCase(getRealEstates.pending, (state) => {
state.isLoading = true
})
builder.addCase(getRealEstates.fulfilled, (state, action) => {
state.isLoading = false
state.isSuccess = true
state.realestate = action.payload
})
builder.addCase(getRealEstates.rejected, (state, action) => {
state.isLoading = false
state.isError = true
state.message = action.payload
})
}
})
export const { reset } = estateSlice.actions
export default estateSlice.reducer
Store
export const store = configureStore({
reducer: {
realestate: realestateReducer,
registered: registerReducer,
},
});
Component
const realestates = useSelector(state => state.realestate)
const { isLoading, realestate, isError, message, isSuccess} = realestates
const dispatch = useDispatch()
useEffect(() => {
dispatch(getRealEstates())
if(realestate){
setShow(true)
}else{
console.log('No data retrieved')
}
}, [dispatch, isError, realestate, message])
It's extraReducers with an uppercase R, your code contains extrareducers.

Trying to pass action.payload from one slice to another different slice

What I am trying to achieve is sending action payload from one slice to another and I have been stuck several hours trying to do so.
I have tried accessing the global store but the problem is I am getting errors on doing so
I am using redux-tool-kit to manage the state of my react application and I am trying to pass a payload from one slice to another, the following is my first slice:
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import axios from 'axios';
import { clearAlert, displayIncorrectEmail } from "./features.js/Alert";
const initialState = {
user: user ? JSON.parse(user) : null,
isMember: false,
isLoading: true
}
This section still for the first slice
export const getRegisteredUser = createAsyncThunk('auth/getRegistrationRes', async (currentUser, thunkAPI) => {
try {
const response = await axios.post('/api/v1/auth/register', currentUser)
return response.data
} catch (error) {
// console.log(error.message)
thunkAPI.rejectWithValue(error.message)
}
})
export const getLoginUser = createAsyncThunk('auth/getLoginRes', async (currentUser, thunkAPI) => {
try {
const response = await axios.post('/api/v1/auth/login', currentUser)
thunkAPI.dispatch(displaySuccess())
setTimeout(() => {
thunkAPI.dispatch(clearAlert())
}, 3000);
return response.data
} catch (error) {
thunkAPI.dispatch(displayIncorrectEmail())
// console.log(error.response.data.msg);
thunkAPI.rejectWithValue(error.message)
//the below return is the action-payload I want to pass to another slice
return error.response.data.message
//
}
})
const authenticationSlice = createSlice({
name: 'auth',
initialState,
reducers: {
},
extraReducers: {
// login user reducers
[getLoginUser.pending]: (state) => {
state.isLoading = true;
},
[getLoginUser.fulfilled]: (state, action) => {
state.isLoading = false;
// console.log(action.payload.getState());
// action.payload.load = true
state.user = action.payload.user
},
[getLoginUser.rejected]: (state) => {
state.isLoading = false;
state.user = null
},
}
})
export const { registerUser, loginUser } = authenticationSlice.actions
export default authenticationSlice.reducer
This is the second slice is the code below
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
showAlert: false,
alertText: '',
alertType: '',
}
const alertSlice = createSlice({
name: 'alert',
initialState,
reducers: {
displayIncorrectEmail: (state, action) => {
state.showAlert = !state.showAlert
//I want to pass the action.payload to this instead of hard-coding it to 'incorrect email' //below
state.alertText = 'incorrect email'
//the state.alertText above
state.alertType = "danger"
},
clearAlert: (state) => {
// setTimeout(() => {
state.showAlert = !state.showAlert;
// }, 4000);
}
}
})
export const { displayDanger, clearAlert, displaySuccess, displayIncorrectEmail } = alertSlice.actions
export default alertSlice.reducer
Kindly help if you have an idea on how to sort this.
cheers.
Just add an extraReducer for getLoginUser.rejected to the second slice as well. You can add that to the extraReducers of as many slices as you want to.
By the way, you really should not be the map object notation of extraReducers, but the extraReducers "builder callback" notation. The object notation you are using is soon going to be deprecated and we have been recommending against it in the docs for a long time.

how to use redux toolkit with firestore in react native

I am using redux toolkit and firebase firestore for backend. I just want to get an array of objects from the database. Below is the code for slice. When I log the payload in the console I am unable to get the data. Thanks in advance.
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import firestore from '#react-native-firebase/firestore';
export const getCarouselImages = createAsyncThunk("/carouselImages", () =>
firestore().collection('Users').get()
);
const initialState = {
isLoading: false,
failed: true,
success: false,
imageArray:[],
};
const carouselData = createSlice({
name: "carouselImageSlice",
initialState,
reducers: {
resetCarouselData: () => initialState,
},
extraReducers: (builder) => {
builder.addCase(getCarouselImages.fulfilled, (state, { payload }) => {
state.isLoading = false;
state.failed = false;
state.success = true;
payload.forEach(doc => {
state.userData.push(doc.data())
});
console.log(payload)
});
builder.addCase(getCarouselImages.rejected, (state, action) => {
state.isLoading = false;
state.failed = true;
state.success = false;
});
builder.addCase(getCarouselImages.pending, (state, { payload }) => {
state.isLoading = true;
state.failed = false;
state.success = false;
});
},
});
export const { resetCarouselData } = carouselData.actions;
export default carouselData.reducer;

Resources