why is this redux toolkit users variable always undefined? - reactjs

I'm trying to add a user to state.users in a redux toolkit slice. Here's userManagementSlice.js:
import { createSlice } from '#reduxjs/toolkit'
const initialState = {
users: [],
}
export const userManagementSlice = createSlice({
name: 'userManagement',
initialState,
reducers: {
addUser: (state, action) => {
const user = action.payload;
state.users.push(user);
},
},
})
// Action creators are generated for each case reducer function
export const { addUser } = userManagementSlice.actions
export default userManagementSlice.reducer
Here's key code from a component that listens to the users in global state and attempts to add a user to that array via dispatch:
const [firstName, setFirstName] = useState();
const [lastName, setLastName] = useState();
const users = useSelector((state) => state.users)
const dispatch = useDispatch()
const addUser2 = () => {
const user = {
firstName,
lastName
}
dispatch(addUser(user));
}
However, the USERS UPDATED value logged to the console from the component is always undefined:
useEffect(() => {
console.log('USERS UPDATED: ' + users);
// reset inputs
setFirstName('');
setLastName('');
}, [users]);
Here's the configureStore setup:
import { configureStore } from '#reduxjs/toolkit'
import userManagementReducer from '../features/userManagement/userManagementSlice'
export const store = configureStore({
reducer: {
userManagement: userManagementReducer,
},
})
Any idea what I'm doing wrong here?

You are mounting the slice at userManagement, so your selector would have to be
const users = useSelector((state) => state.userManagement.users)

Related

Async thunk breaks react app in useEffect

I’ve been working on a react crud application to teach myself redux toolkit, and I can’t seem to get a particular async function to work. I’m trying to fetch recipes from a certain user from firebase - this function worked when I was just playing around and didn’t have any users or authentication (i.e., the database was just recipes), but now that I have authentication and users, I cannot fetch or display the recipes, and the recipes are not added to the recipes state array. Thanks to redux logger, I can see that the recipes are accessed from firebase, but they're not displayed or added to the recipes state. Once I refresh the page, the application breaks. I can add and delete recipes to a user in firebase, though.
Here’s what the async thunk looks like:
'user/getRecipes',
async(uid, thunkAPI) => {
const snapshot = await getDocs(collection(db, `users/${uid}/recipes`))
const array = []
snapshot.forEach((doc) => {
array.push(doc.data())
})
return array
}
)
Here’s where I call it inside a useEffect. I’ve tried using the uid as an argument and getting the uid with getState() inside the thunk - it doesn’t seem to make a difference but maybe I’m doing that wrong, as well?
const RecipeApp = () => {
const dispatch = useDispatch()
const recipes = useSelector((state) => state.recipes)
const auth = getAuth()
const navigate = useNavigate()
const [user, loading, error] = useAuthState(auth)
const uid = useSelector(state => state.users.uid.user)
useEffect(() => {
if (loading) return;
if (!user) return navigate("/");
}, [user, loading])
useEffect(() => {
dispatch(getRecipes(uid))
console.log(uid)
}, [dispatch])
const addRecipe = (name, ingredients, instructions, notes) => {
const date = new Date()
const newRecipe = {
name: name,
ingredients: ingredients,
instructions: instructions,
notes: notes,
recipeId: uuidv4(),
date: date.toLocaleDateString(),
createdAt: Date.now(),
}
dispatch(addRecipeToFirestoreAndRedux(newRecipe))
}
return (
<div className="recipeapp">
<h1 className='recipeapp__heading'>Recipes</h1><button onClick={logout}>Log out</button>
<div className="recipeapp__container">
<ErrorBoundary
FallbackComponent={ErrorFallBack}>
<RecipeList
recipes={recipes}
/>
</ErrorBoundary>
<AddRecipe
addRecipe={addRecipe}
/>
</div>
</div>
);
}
export default RecipeApp;
Here’s what the relevant code (and a little more) of the recipes reducer looks like:
export const recipeSlice = createSlice({
name: 'recipesSlice',
initialState: {
recipes: []
},
reducers: {
ADD_RECIPE: (state, action) => {
state.recipes.push(action.payload)
},
DELETE_RECIPE: (state, action) => {
state.recipes = state.recipes.filter((recipe) => recipe.recipeId !== action.payload.recipeId)
},
extraReducers: builder => {
builder.addCase(getRecipes.fulfilled, (state, action) => {
state.recipes = action.payload
})
}
})
export const { ADD_RECIPE, DELETE_RECIPE } = recipeSlice.actions;
export default recipeSlice.reducer
And here's what the store.js file looks like:
import { configureStore, combineReducers } from "#reduxjs/toolkit";
import recipeReducer from './features/recipeslice'
import userReducer from './features/authentication'
import thunk from "redux-thunk";
import logger from "redux-logger";
const rootReducer = combineReducers({
recipes: recipeReducer,
users: userReducer
})
export const store = configureStore({
reducer: rootReducer,
middleware: [thunk, logger]
})
Apologies if this is overkill. Hopefully this makes sense, and thank you in advance for any and all help!

invalid hook call usedispatch in nextjs

I keep getting this error but it's not clear why. It is a functional component and the hook is within the body. I tried searching around for an answer but it's not clear why it's having an issue.
import { useDispatch } from "react-redux";
import { selectItems } from "../pages/slices/cartSlice";
import { addToBasket } from "../pages/slices/cartSlice";
import ShoppingCartIcon from "#mui/icons-material/ShoppingCart";
import axios from "axios";
code
export default function Products({ data }) {
const dispatch = useDispatch();
const [item, setItem] = useState({});
const pushProducts = () => {
setItem(data.data.products.data);
}
const items = useSelector(selectItems);
const addCart = () => {
const product = {
description,
quantity,
price,
};
//send product to basket slice
dispatch(addToBasket(product));
};
This is the slice
cartSlice
import { createSlice } from '#reduxjs/toolkit'
const initialState = {
items: [],
};
const cartSlice = createSlice({
name: "basket",
initialState,
reducers: {
addToBasket: (state, action) => {
state.items = [...state.items, action.payload]
},
removeFromBasket: (state, action) => {},
},
});
export const {addToBasket, removeFromBasket} = cartSlice.actions
export const selectItems = (state) => state.basket.items;
export default cartSlice.reducer

React redux state does not mutate state fast enough on login to ensure that user type specific routing enables the correct Routes

I am building a React redux app which on login updates the redux initialState. Based on the user type stored in redux certain routes are enabled. Unfortunately the state mutation of redux seems to be not fast enough, since on redirect from the login the route is not available. I tried various timeouts, which - however - did not work.
Here you have an excerpt from my authSlice in redux
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import {} from '../services/auth.service';
const initialState = {
loggedIn: false,
userData: {}
};
export const loginAsync = createAsyncThunk(
'auth/login',
async (credentials) => {
return await login(credentials);
}
);
export const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
},
extraReducers: (builder) => {
builder
.addCase(loginAsync.fulfilled, (state, action) => {
state.userData = action.payload.userData;
state.loggedIn = true;
})
},
});
export const selectIsLoggedIn = (state) => state.auth.loggedIn;
export const selectUserData = (state) => state.auth.userData;
export const selectUserType = (state) => state.auth.userData.userType;
export default authSlice.reducer;
The login component looks the following
import React, {useEffect, useState} from "react";
import {Link, Redirect} from 'react-router-dom';
import {selectIsLoggedIn, selectUserType, loginAsync, selectUserData, setUserData} from "../../../slice/authSlice";
import { useDispatch, useSelector } from "react-redux";
import { history } from "../../../helpers/history";
import '../../../styles/main/Login.css';
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const dispatch = useDispatch();
//Change handlers
const changeEmail = (e) => {
setEmail(e.target.value);
};
const changePassword = (e) => {
setPassword(e.target.value);
};
/**
* #function (01) prevent the default form behaviour and set to loading
* (02) dispatch the login
* (03) redirect based on user type
*/
const performLogin = (e) => {
//01
e.preventDefault();
setLoading(true);
//02
dispatch(loginAsync({email: email, password: password}))
.then((res) => {
//03
res.payload.userData.userType === 'student' ? history.push("/student/search/") : res.payload.userType === 'coder' ? history.push("/coder/editor/") : history.push("/admin/invite/");
window.location.reload();
})
.catch(() => {
setLoading(false);
});
};
return (
<div className="login_wrapper">
login npx stuff...
</div>
);
};
export default Login;
The Routes are based on a use selector redux operation where I obtain the loggedin state and user type. This routes component is directly included in the App component.
If I set the initialState to the respective states (loggedin true and usertype = student) everything works fine.
Any help is highly appreciated

Redux Toolkit: Error: Actions must be plain objects. Use custom middleware for async actions

I have crated the following userSlice
import { createSlice } from "#reduxjs/toolkit";
const initialState = {username: ""};
export const userSlice = createSlice({
name: "user",
initialState,
reducers: {
setUsername(state, action) {
state.username = action.payload;
},
},
});
export const { setUsername } = userSlice.actions;
export default userSlice.reducer;
Now, I wanted to use the dispatch the reducer setUsername like this
const Login = (props) => {
const classes = useStyles();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const user = useSelector((state) => state.user.username);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getUsers());
}, [dispatch]);
const users = useSelector((state) => state.users.users);
const validateUser = () => {
const usernames = Object.keys(users);
if (usernames.includes(username)) {
if (users[username].password === password) {
console.log(username);
dispatch(
setUsername({
payload: username,
})
);
}
}
};
But this gives me the error Actions must be plain objects. Use custom middleware for async actions.. After trying to resolve this issue for one hour I must admit that I have no clue how to resolve this problem. What am I doing wrong?
//store.js
import { configureStore } from "#reduxjs/toolkit";
import userReducer from "../features/user/userSlice";
import usersReducer from "../features/users/usersSlice";
export const store = configureStore({
reducer: {
user: userReducer,
users: usersReducer,
},
});
The error in the code comes from non unique names in the reducer setUsername and the state setter setUsername from the useState("") function.

My async actions endlessly called in my react redux toolkit slice

My Redux-async function endlessly called and hangs my system when i subscribe to the store using useSelector.
My Product.Slice
import { createSlice } from '#reduxjs/toolkit'
import { apiCallBegan } from './../../app/store/api/api.action';
const productSlice = createSlice({
name: 'product',
initialState: {
products: [],
},
reducers: {
productsReceived: (state, action) => {
state.products=action.payload
}
}
})
export const { addProducts,productsReceived } = productSlice.actions
export const loadProducts = () => apiCallBegan({
url: "/product/",
method: "get",
onSuccess: productsReceived.type
})
export const getProducts = (state) => state.product.products
export default productSlice.reducer
ProductList.js Use Reducer implementation:
const dispatch = useDispatch()
dispatch(loadProducts())
const products=useSelector(state=>state.product.products)
console.log(products)
I assume this is the problem:
const dispatch = useDispatch();
dispatch(loadProducts());
const products = useSelector(
(state) => state.product.products
);
console.log(products);
That is in a component that re renders when products change while also changing the products. Maybe you can only get the products on mount:
const dispatch = useDispatch();
React.useEffect(() => dispatch(loadProducts()), [
dispatch,
]);

Resources