I am using a redux slice in a react project to store information about the current user. However, I am getting the following error when loading the page:
TypeError: Cannot read properties of undefined (reading 'name')
Dashboard.js:
export function Dashboard() {
const currentUser = useSelector(state => state.currentUser);
const dispatch = useDispatch();
return (
<div>
<p>{currentUser.name}</p>
<p>{currentUser.description}</p>
</div>
<Button onClick={() => dispatch(setCurrentUser(name, description))}>Set user</Button>
);
}
currentUserSlice.js:
import { createSlice } from '#reduxjs/toolkit'
export const currentUser = createSlice({
name: 'currentUser',
initialState: {
name: "",
description: ""
},
reducers: {
setCurrentUser: (state, action) => {
return state = {
name: action.payload.name,
description: action.payload.description
}
}
}
})
// each case under reducers becomes an action
export const { setCurrentUser } = currentUserSlice.actions
export default currentUserSlice.reducer
store.js:
import { configureStore } from '#reduxjs/toolkit'
import currentUserReducer from './currentUserSlice'
export default configureStore({
reducer: {
currentUser: currentUserReducer,
}
})
Update
I did not properly import the reducer into store.js. However, the dispatch is not updating the data now that I have imported it
You are sending two values in your payload instead of an object. Please update your dispatched function to send an object instead:
export function Dashboard() {
const currentUser = useSelector(state => state.currentUser);
const dispatch = useDispatch();
return (
<div>
<p>{currentUser.name}</p>
<p>{currentUser.description}</p>
</div>
<Button onClick={() => dispatch(setCurrentUser({name, description}))}>Set user</Button>
);
}
Related
Undefined is returned while trying to access the state.
Slice:
const initialState = { valid: false };
const validNameSlice = createSlice({
name: "validName",
initialState,
reducers: {
makeNameValid(state) {
state.valid = true;
},
makeNameInvalid(state) {
state.valid = false;
}
}
});
export const {
makeNameValid,
makeNameInvalid
} = validNameSlice.actions;
export default validNameSlice.reducer;
Combining the reducers : (there is actually more than one reducers)
import { combineReducers } from "redux";
import validSlice from "./validSlice";
const reducers = {
validSlice
};
const rootReducer = combineReducers(reducers);
export default rootReducer;
Store:
import { configureStore } from "#reduxjs/toolkit";
import rootReducer from "./rootReducers";
const store = configureStore({
reducer: {
rootReducer
}
});
export default store;
Component:
import { makeNameValid, makeNameInvalid } from "./validSlice";
import { connect } from "react-redux";
class App extends React.Component {
handleClick = () => {
console.log(this.props.isValidName);
};
handleDispatch = () => {
this.props.makeNameValid();
};
handleDispatchFalse = () => {
this.props.makeNameInvalid();
};
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button onClick={() => this.handleClick()}>test</button>
<button onClick={() => this.handleDispatch()}>Make true</button>
<button onClick={() => this.handleDispatchFalse()}>Make false</button>
</div>
);
}
}
const mapStateToProps = (state) => ({
isValidName: state.validSlice
});
const mapDispatchToProps = (dispatch) => ({
makeNameValid: () => dispatch(makeNameValid()),
makeNameInvalid: () => dispatch(makeNameInvalid())
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
Initially without root reducer it worked, but with root reducer the state in log is returning undefined.
Link to codesandbox : https://codesandbox.io/s/holy-shadow-ii8fs9?file=/src/App.js
You have a tiny error in your configuration - you're wrapping up your rootReducer, but you just need to pass it as the reducer parameter:
const store = configureStore({
reducer: rootReducer,
});
I've been at this for a while and I can't figure it out. When I dispatch my populateDatasets action I can see that my store gets updated just fine in dev tools, but when I try to to access that state in a React component with useSelector it always returns undefined.
Here is how I've configured my store
import { configureStore } from '#reduxjs/toolkit'
import datasetReducer from './datasets'
export default configureStore({
reducer: {
datasets: datasetReducer
},
devTools: true
})
Here is the slice that I've created for this piece of state
import { createSlice } from '#reduxjs/toolkit'
export const datasetSlice = createSlice({
name: 'datasets',
initialState: [],
reducers: {
populateDataset: (state, action) => {
state.push(action.payload)
}
}
})
export const { populateDataset } = datasetSlice.actions
export default datasetSlice.reducer
And here is where I dispatch the action in my React component
const App = () => {
const { datasets } = useSelector((state) => state.datasets)
console.log('datasets: ' + datasets)
const dispatch = useDispatch()
useEffect(() => {
csv(FantraxHQData).then(data => {
data.map((player) => {
player.isDrafted = false
return player
})
dispatch(populateDataset(data))
})
csv(FantasyProsData).then(data => {
data.map((player) => {
player.isDrafted = false
return player
})
dispatch(populateDataset(data))
})
}, [])
return (
<div className={styles.outter}>
<MyTeam />
<div className={styles.container}>
<DataButtons />
<DraftBoard />
</div>
</div>
)
}
Again, my store updates just fine when I dispatch the action, but datasets is always undefined. Any help would be much appreciated.
Update Solution: The solution was to change { datasets } to datasets
Your datasets is an array in your redux store, but you're reading it as a object when
you're using useSelector(). Change the useSelector line to const datasets = useSelector((state) => state.datasets). Don't use the flower bracket on datasets.
in below react/ redux toolkit app , in userslice file I am trying to export my entities piece of state and import in main file , when I try to console in comes undefined , not sure why its undefined ,
but When I trying to pull the {entities} directly form state its working fine, would like to know why its showing undefined in console, if anyone knows please check ?
below is the state part which I am getting undefined
export const { SelectUserList } = (state) => state.userslist.entities;
below is the console which shows undefiend
console.log(SelectUserList);
my slice file is below
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
export const fetchuserlist = createAsyncThunk(
"userslist/fetchusers",
async () => {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
const users = await response.json();
return users;
}
);
const userSlice = createSlice({
name: "userslist",
initialState: {
entities: [],
loading: false,
},
reducers: {
// userAdded(state, action) {
// state.entities.push(action.payload);
// },
},
extraReducers: {
[fetchuserlist.pending]: (state, action) => {
state.loading = true;
},
[fetchuserlist.fulfilled]: (state, action) => {
state.entities = [...state.entities, ...action.payload];
state.loading = false;
},
[fetchuserlist.rejected]: (state, action) => {
state.loading = false;
},
},
});
export const { userAdded, userUpdated, userDeleted } = userSlice.actions;
export const { SelectUserList } = (state) => state.userslist.entities;
export default userSlice.reducer;
me component file is below
import React from "react";
import { fetchuserlist, SelectUserList } from "./features/userSlice";
import { useDispatch, useSelector } from "react-redux";
const Mainlist = () => {
const dispatch = useDispatch();
const { entities } = useSelector((state) => state.users);
console.log(SelectUserList);
return (
<div>
<button onClick={() => dispatch(fetchuserlist())}>Load list</button>
{entities?.map((s) => (
<div className="user_list" key={s.id}>
<h4>{s.id}</h4>
<h6>{s.email}</h6>
<button>delete</button>
<button>edit</button>
</div>
))}
</div>
);
};
export default Mainlist;
In your slice you are declaring the function in the wrong way. You should declare the SelectUserList function like this:
export const SelectUserList = (state) => state.userslist.entities;
In your component file you should access the entities returned in SelectUserList with a useSelector. Like this:
const usersListEntities = useSelector(SelectUserList);
I am creating a react app, when getting data from redux. I am facing the below error message in browser. please check and let me know what am I missing.
I am using create-react-app redux-toolkit setup template to create the app
Here is my app.js:
import React from "react";
import { useSelector } from "react-redux";
import "./App.css";
import { selectUser } from "./features/userSlice";
import Header from "./components/Header";
import Sidebar from "./components/Sidebar";
import Feed from "./components/Feed";
import Login from "./components/Login";
function App() {
const user = useSelector(selectUser);
return (
<div className="App">
<Header />
{ !user ? (
<Login />
) : (
<div className="main_content">
<Sidebar />
<Feed />
</div>
)}
</div>
);
}
export default App;
below you can find the redux reducer and actions
import { createSlice } from '#reduxjs/toolkit';
export const userSlice = createSlice({
name: 'user',
initialState: {
user: null,
},
reducers: {
login: (state, action) => {
state.value = action.payload
},
logout: (state, action) => {
state.user = null
}
},
});
export const { login, logout } = userSlice.actions;
export const selectUser = (state) => state.user.user;
export default userSlice.reducer;
below is the screenshot of error which. I'am getting when running the app
Working example for you, be sure you configured your store correctly. You should separate this into responding files.
import React from "react";
import { combineReducers, createStore, createSlice } from "#reduxjs/toolkit";
import { connect, Provider, useDispatch, useSelector } from "react-redux";
// your part
const userSlice = createSlice({
name: "user",
initialState: {
user: null
},
reducers: {
login: (state, action) => {
state.user = action.payload;
},
logout: (state, action) => {
state.user = null;
}
}
});
const { login, logout } = userSlice.actions
const selectUser = (state) => state.user.user;
// what I added
const reducer = combineReducers({
user: userSlice.reducer
});
const store = createStore(reducer);
const Main = (props) => {
const dispatch = useDispatch() // I used this to check if reducers work
const user = useSelector( selectUser )
return (
<div onClick={ () => { dispatch(login({name: "Adam"})) }}>
{ !user ? "LOGIN" : "DASHBOARD "}
</div>
)
}
const mapStateToProps = (state) => ({
user: state.user
});
const Container = connect(mapStateToProps, { login, logout })(Main);
function App() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
export default App;
I am trying to implement a simple login form, that gets username and password as input.
User.js
import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {
set,
reject,
verify,
isLoggedIn,
} from './userSlice';
export function User() {
const isUserLoggedIn = useSelector(isLoggedIn);
const dispatch = useDispatch();
const [loginError, setLoginError] = useState(false);
return (
<div>
<div> {loginError? 'Invalid credentials': ''}</div>
{/* Form elements here */}
<button
onClick={() => dispatch(verify())}
>
Verify User
</button>
</div>
);
}
userSlice.js
import { createSlice } from '#reduxjs/toolkit';
export const userSlice = createSlice({
name: 'user',
initialState: {
loggedIn: false,
email: '',
name: '',
token:''
},
reducers: {
set: (state, action) => {
return Object.assign({}, state, action.payload);
},
reject: (state, action) =>{
state.value = action.payload
}
},
});
export const { set, reject } = userSlice.actions;
export const verify = user => dispatch => { // For making an api call to verify the credentials are correct
axios.post('login', data).then(function(){
dispatch(set({loggedIn:true}))
}).catch(function(){
dispatch(reject({loggedIn:false}))
});
};
export const isLoggedIn = state => state.user.loggedIn;
export default userSlice.reducer;
All codes are working fine.
Now if the api call fails, i need to update the state loginError to true. How it can be done from userSlice.js file to User.js file.
Something like that I guess
User.js
import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {
set,
reject,
verify,
isLoggedIn,
isLoginError, //<-------------
} from './userSlice';
export function User() {
const isUserLoggedIn = useSelector(isLoggedIn);
const dispatch = useDispatch();
const isLoginError = useSelector(isLoginError); //<----------------
return (
<div>
<div> {isLoginError ? 'Invalid credentials': ''}</div> //<-------------
{/* Form elements here */}
<button
onClick={() => dispatch(verify())}
>
Verify User
</button>
</div>
);
}
userSlice.js
import { createSlice } from '#reduxjs/toolkit';
export const userSlice = createSlice({
name: 'user',
initialState: {
loggedIn: false,
email: '',
name: '',
token:''
},
reducers: {
set: (state, action) => {
return Object.assign({}, {...state, loginError:false}, action.payload); //<------
},
reject: (state, action) =>{
return Object.assign({}, {...state, loginError:true}, action.payload); //<-------
}
},
});
export const { set, reject } = userSlice.actions;
export const verify = user => dispatch => { // For making an api call to verify the credentials are correct
axios.post('login', data).then(function(){
dispatch(set({loggedIn:true}))
}).catch(function(){
dispatch(reject({loggedIn:false}))
});
};
export const isLoggedIn = state => state.user.loggedIn;
export const isLoginError = state => state.user.loginError; //<----------
export default userSlice.reducer;