Multiple actions from one redux-toolkit thunk - redux

toolkit, and I'm trying to understand exactly how createAsyncThunk fits in all of this. I want to make a call to my server to get session info and the user's info in one call. However, from what I've seen so far on createAsyncThunk, it only uses a single slice of state? Could someone elaborate on how I could hit both session and user at once? Below is an example to illustrate.
const payload = await myApi.login(loginInfo)
dispatch(loginSession(payload.session)
dispatch(loginUser(payload.user)
export const sessionSlice = createSlice({
name:'session',
initialState,
reducers: {
loginSession: (state,action:PayloadAction<schema.Session>) => {
state = action.payload
},
},
})
export const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
loginUser:(state,action:PayloadAction<schema.User>) => {
if (state !== null) {
throw new Error('a user is already logged in')
}
state = action.payload
},
}
})

const payload = await myApi.login(loginInfo)
dispatch(loginFulfilled({
session: payload.session,
user: payload.user,
})
export const sessionSlice = createSlice({
name:'session',
initialState,
reducers: {
loginFulfilled: (state,action:PayloadAction<{session: schema.Session}>) => {
state = action.payload.session
},
},
})
export const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
loginFulfilled:(state,action:PayloadAction<{user: schema.User>}) => {
if (state !== null) {
throw new Error('a user is already logged in')
}
state = action.payload.user
},
}
})

Related

localStorage.getItem is not working on nextjs, using redux and material ui

i am trying to save user settings, [dark mode/ light mode] on local storage using redux on nextjs
I can save the data on the local storage but i can't pull the data into the initialSatte. here is my code
import { createSlice } from "#reduxjs/toolkit";
import Cookies from "js-cookie";
const getFromLocalStorage = (key: string) => {
if (!key || typeof window === "undefined") {
return "";
}
try {
// #ts-ignore
return JSON.parse(localStorage.getItem(key)) || {};
} catch (error) {
return {};
}
};
export const uiSettings = createSlice({
name: "uiSettings",
initialState: {
theme: getFromLocalStorage("uiSettings")?.theme || "dark",
},
reducers: {
themeSwitch: (state, action) => {
state.theme = action.payload;
window.localStorage.setItem(
"uiSettings",
JSON.stringify({
theme: action.payload,
})
);
},
},
extraReducers: (builder) => {},
});
export const uiSettingsReducer = uiSettings.reducer;
export const { themeSwitch } = uiSettings.actions;
i tried alot of ways to solve it but it didn't work.

How to add cases in ExtraReducer to match the actions created in currentReducer using createSlice() from #reduxjs/toolkit

Here below I have mentioned a redux slice. A fetchAllApps thunk function is created with createAsyncThunk for action 'allApps/allappsAdded/' which I dynamically got by allAppsAdded.type. When the fetchAllapps is dispatched it generated actions of type 'allApps/allappsAdded/pending', 'allApps/allappsAdded/fulfilled', 'allApps/allappsAdded/rejected' which I need to add in extraReducers to handle it by doing hardcode.Is there any way to add these action types like allAppsAdded.type programatically?. so that in future It makes easy for me to change these without redundant..
import {
configureStore,
createAsyncThunk,
createSlice
} from "#reduxjs/toolkit";
const initialState = {
apps: [],
categories: [],
loading: {
apps: false
}
};
const allappsSlice = createSlice({
name: "allapps",
initialState,
reducers: {
allappsAdded: (state, action) => {
state["apps"] = action.payload.apps;
state["categories"] = action.payload.categories;
}
},
extraReducers: {
}
});
export default () =>
configureStore({
reducer: allappsSlice.reducer
});
const { allappsAdded } = allappsSlice.actions;
const fetchAllApps = createAsyncThunk(allappsAdded.type, async () => {
console.log("ss");
setTimeout(() => ({ apps: [], categories: [] }), 2000);
});
export { allappsAdded, fetchAllApps };

Redux state doesn't update immediately

I have super simple question
Why my redux state doesn't update immediately?
const { reducer, actions } = createSlice({
name: "professionals",
initialState: {
loading: false,
lastFetchList: undefined,
list: undefined,
professional: undefined,
filters: {
virtual: false
}
},
reducers: {
professionalsListRequested: (professionals, action) => {
if (action.payload.withLoading) professionals.loading = true;
},
professionalsListRequestFailed: (professionals, action) => {
professionals.loading = false;
},
professionalsListReceived: (professionals, action) => {
professionals.lastFetchList = Date.now();
professionals.list = action.payload.data.dataArr;
professionals.loading = false;
},
virtualUpdated: (categories, action) => {
categories.filters.virtual = action.payload;
}
},
});
export const { virtualUpdated } = actions;
export default reducer;
it is my slice.
and here is code of the component :
const dispatch = useDispatch();
const filters = useSelector((state) => state.professionals.filters);
const handlePressOnVirtual = async () => {
console.log("Before" , filters.virtual)
await dispatch(virtualUpdated(!filters.virtual));
console.log("after" , filters.virtual)
};
when handlePressOnVirtual function is called the both console.log(s) print previous value of the state.
When you are still in handlePressOnVirtual function, you are still in a closure, so all the references will still be your existing filters
So you would need to wait for another re-render for useSelector to invoke again then the new values will come.
One way to see the latest changes is to put your log inside a useEffect:
useEffect(() => {
console.log("after" , filters.virtual)
},[filters.virtual]);

converting to redux tool kit and getting "Unhandled Rejection (TypeError): state.push is not a function"

i'm stuck while converting an old project to redux tool kit getting an "Unhandled Rejection (TypeError): state.push is not a function" error. i haven't got to grips with the action/thunk and reducer immutability yet. The alerts are working but then the error msg.
import axios from 'axios';
import { setAlert } from '../alerts/alertSlice';
const slice = createSlice({
name: 'auth',
initialState: {
token: localStorage.getItem('token'),
isAuthenticated: null,
loading: true,
user: null,
},
reducers: {
registerSuccess: (state, action) => {
const { payload } = action.payload;
state.push({
payload,
isAuthenticated: true,
loading: false,
});
},
registerFail: (state, action) => {
localStorage.removeItem('token');
state.push({
token: null,
isAuthenticated: false,
loading: false,
user: null,
});
},
},
});
const { registerSuccess, registerFail } = slice.actions;
export default slice.reducer;
// Register User
export const register =
({ name, email, password }) =>
async (dispatch) => {
const config = {
headers: {
'comtent-Type': 'application/json',
},
};
const body = JSON.stringify({ name, email, password });
try {
const res = await axios.post('/api/users', body, config);
dispatch({
type: registerSuccess,
payload: res.data,
});
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')));
}
dispatch(registerFail());
}
};
.push is an array function to add a new item at the end of an array - your state is not an array.
You probably wanted to do something along the lines of
state.token = null
state.isAuthenticated = false
state.loading = false
state.user = null

Redux and Firebase

I've struggled to implement react-redux-firebase and redux-firestore into my app after configuring the redux store (struggled with this too, even though redux-toolkit simplified some things). Is it possible that I can communicate with firebase without using those two packages above? If so, how do I use firebase in any of my slices? e.g., auth slice below.
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
import firebase from 'firebase/app';
export const authSlice = createSlice({
name: 'authSlice',
initialState: {
currentUser: null,
isLoggedIn: false,
isLoading: false,
},
reducers: {
login: async (state, action) => {},
registerUser: (state, action) => {},
changeProfile: (state, action) => {},
logout: async (state, action) => {},
setCurrentUser: (state, action) => {},
},
});
// Action creators are generated for each case reducer function
export const {
login,
registerUser,
changeProfile,
logout,
setCurrentUser,
} = authSlice.actions;
export default authSlice.reducer;
This is the query in a separate file.
import firestore from '#react-native-firebase/firestore';
export const getPopularProducts = firestore()
.collection('POPULAR')
.orderBy('count', 'desc')
.limit(10)
.get()
.then(querySnapshot => {
const views = [];
querySnapshot.forEach(doc => {
views.push({
key: doc.id,
count: doc.data().count,
product: doc.data().product,
});
});
return views;
})
.catch(error => {
alert('Error getting popular products: ', error);
});
In the reducer/slice, import getPopularProducts.
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
import {getPopularProducts} from './../../lib/fetchData';
// Initial states
const initialState = {
products: [],
mainList: [],
popular: [],
};
// Get popular products from firebase
export const fetchPopularProducts = createAsyncThunk(
'prodSlice/fetchPopularProducts',
async () => {
const data = getPopularProducts;
const {_W} = data;
if (_W !== null) {
return _W;
}
},
);
export const productSlice = createSlice({
name: 'prodSlice',
initialState,
reducers: {
fetchData: (state, action) => {
state.isLoading = true;
state.mainList = action.payload;
state.products = action.payload;
}
},
extraReducers: {
[fetchPopularProducts.fulfilled]: (state, action) => {
state.popular = action.payload;
},
},
});
// Action creators are generated for each case reducer function
export const {fetchData} = productSlice.actions;
export const selectProducts = state => state.prodSlice;
export default productSlice.reducer;
Then you dispatch fetchPopularProducts inside the useEffect hook. I cases where I needed a parameter for the query, I'd put the query inside createAsyncThunk.

Resources