When i click the delete button the action(removeBus) is dispatched and the correct payload value is sent however the store is not updating
here is my busSlice file
import { createSlice } from "#reduxjs/toolkit";
const initialState = [
{
id: "0",
description: "bus1"
},
{
id: "1",
description: "bus2"
}
];
const busSlice = createSlice({
name: "bus",
initialState,
reducers: {
removeBus: (state, action) => {
state.filter((bus) => bus.id !== action.payload);
console.log(action.payload);
}
}
});
export const { removeBus } = busSlice.actions;
export default busSlice.reducer;
here is where i created and export the store
import { configureStore } from "#reduxjs/toolkit";
import busReducer from "../features/busSlice";
export const store = configureStore({
reducer: {
busses: busReducer
}
});
the component that uses the state is created in the following code and App is wrapped inside a redux provider Component and the above store is provided as a props in index.js
import "./styles.css";
import { useSelector, useDispatch } from "react-redux";
import { removeBus } from "./features/busSlice";
export default function App() {
const dispatch = useDispatch();
const busses = useSelector((state) => state.busses);
const handleRemove = (id) => {
dispatch(removeBus(id));
};
return (
<div className="App">
{busses.map((bus) => (
<ul>
<li key={bus.id}>
<p>{bus.description}</p>
<button onClick={() => handleRemove(bus.id)}>delete</button>
</li>
</ul>
))}
</div>
);
}
using redux dev tools i can see an action busses\removeBus is dispatched with the appropriate payload value but nothing is happening to the state.
You can change:
removeBus: (state, action) => {
state.filter((bus) => bus.id != action.payload);
console.log(action.payload);
}
can be the data types of bus.id and action.payload are different.
you are not updating state correctly you need to say change my state to filtered state
like this
removeBus(state, action){
state = state.filter((bus) => bus.id !== action.payload);
console.log(action.payload);
}
I also had this issue.
I my case, code looked like this:
const someSlice = createSlice({
name: 'test',
initialState: null,
reducers: {
addData(state, action) {
state = action.payload
}
}
});
This did not work
const someSlice = createSlice({
name: 'test',
initialState: null,
reducers: {
addData(state, action) {
state = action.payload;
return state;
}
}
});
this worked.
But this is very strange, should not work this way.
You need to return your updated states
import { createSlice } from "#reduxjs/toolkit";
const initialState = [
{
id: "0",
description: "bus1"
},
{
id: "1",
description: "bus2"
}
];
const busSlice = createSlice({
name: "bus",
initialState,
reducers: {
removeBus: (state, action) => {
state = state.filter((bus) => bus.id !== action.payload);
return state;
}
}
});
export const { removeBus } = busSlice.actions;
export default busSlice.reducer;
Related
I am very new to RTK, so I am trying to create a store and a slicer.
At first, at least I want to fetch some data from an API so when it start loading and after being succeed, I know the state of it.
Here I am creatinf the slicer:
const initialState: PlayerState = {
players: [],
status: 'idle'
};
export const getPlayers = createAsyncThunk('players/getPlayers', async () => {
const response = await axios.get(
'https://6360055fca0fe3c21aaacc04.mockapi.io/player'
);
return response.data;
});
const playerSlice = createSlice({
name: 'players',
initialState,
reducers: {
addPlayer: (state, action: PayloadAction<IPlayerProps>) => {
console.log('done');
state.players.push(action.payload);
}
},
extraReducers: {
[getPlayers.pending]: (state, action) => {
console.log('loading');
state.status = 'loading';
},
[getPlayers.fulfilled]: (state, action) => {
console.log('succeeded');
state.status = 'succeeded';
state.players = state.players.concat(action.payload);
}
}
});
export const { addPlayer } = playerSlice.actions;
export const selectPlayers = (state: RootState) => state.players.payload;
And here I am trying to connect it to the store:
//#ts-nocheck
import { configureStore } from '#reduxjs/toolkit'
import { addPlayer } from './playerSlice'
export const store = configureStore({
reducer: {
players: addPlayer,
},
})
export type RootState = ReturnType<typeof store.getState>;
So, after that I have a page with a button, so when I click it I try to dispatch something out of it with no luck unfortunately:
const NextPage = () => {
const dispatch = useDispatch();
return (
<ButtonNext
onClick={() => {
dispatch(addPlayer);
}}
text="< Back"
/>
);
};
export default NextPage;
Any help would be appreciated! :)
The are several issues in your code
First fix your createAsyncThunk
export const getPlayers = createAsyncThunk('players/getPlayers'
async (_unusedArgs, _thunkApi) => {
const response = await fetch('http://localhost:3000/players')
return response.json()
}
)
Your slice should look like this, note the builder callbacks for the cases:
export const playerSlice = createSlice({
name: "players",
initialState,
reducers: {
addPlayer: (state, action) => {
console.log("done");
state.players.push(action.payload);
}
},
extraReducers: (builder) => {
builder.addCase(getPlayers.fulfilled, (state, action) => {
console.log(action.payload);
state.players = action.payload;
state.status = "idle";
});
builder.addCase(getPlayers.pending, (state, action) => {
console.log("loading");
state.status = "loading";
});
}
});
export default playerSlice.reducer;
Call it inside the anonymous fn
<ButtonNext
onClick={() => {
dispatch(getPlayers()); // call with no arguments.
}}
text="< Back"
/>
And I also think that your root reducer in store is not right
import playerSlice from './playerSlice' // defaulted export
export const store = configureStore({
reducer: {
players: playerSlice,
},
})
Please check this sandbox with working example: https://codesandbox.io/s/redux-toolkit-basic-players-w-pokemons-6wmjm0?file=/src/features/playerSlice.js:409-995
I have 2 slices, the first of which contains state errors and the second of which contains logic.
Is it possible to change the value state in the error slice from a logical slice?
Error slice
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
error: false,
};
export const errorSlice = createSlice({
name: "error",
initialState,
reducers: {
setError: (state, action) => {
state.error = action.payload;
},
},
});
export const { setError } = errorSlice.actions;
export default errorSlice.reducer;
Logical slice
import { createSlice } from "#reduxjs/toolkit";
export const doSomething = (data) => {
return (dispatch) => {
dispatch(setData(data.text))
// here I want dispatch setError from errorSlice
// dispatch(setError(data.error))
};
};
const initialState = {
data: null,
};
export const logicalSlice = createSlice({
name: "logical",
initialState,
reducers: {
setData: (state, action) => {
state.error = action.payload;
},
},
});
export const { setData } = logicalSlice.actions;
export default logicalSlice.reducer;
And I need to run it from a component with a single dispatch
dispatch(doSomething(data))
Is there such a possibility?
Thank you!
I have this reducer
import { createSlice } from "#reduxjs/toolkit";
export const projectSlice = createSlice({
name: "project-redux",
initialState: {
name: "",
},
reducers: {
get_project: (state, action) => {
axios
.get("http://localhost:5000/api/project/" + action.payload)
.then((res) => {
state = res.data; //which contains the name
});
},
},
});
export const { get_project } = projectSlice.actions;
export default projectSlice.reducer;
and want to access the "name" with useAppSelector
const dispatch = useAppDispatch();
const {name}=useAppSelector(state=>state.projs) //projs is the name of projectsReducer in the store
console.log(name) // only give initial state
How do I get the 'name' value after the get request is fulfilled?
Solution:
export const fetchProject = createAsyncThunk(
"fetchProject",
async (id) => {
const res = await axios.get("http://localhost:5000/api/project/" + id);
return res.data.title;
}
);
reducers: {//other reducers if ther is}
extraReducers: (builder) => {
builder.addCase(fetchProject.fulfilled, (state, action) => {
state.title = action.payload;
});
},
then:
const name = useAppSelector((state) => state.projs.title);
console.log("selecttitle", name);
You can't put the side effect(I/O operations) code inside reducer functions. The redux reducer function should be pure. You should use createAsyncThunk to fetch data.
After your dispatch the async thunk, you should mutate the state with the fetched data inside extraReducers field of createSlice. After mutating the state, the component will re-render, then the useAppSelector will be called. You will read the state fetched from the remote server from the redux store.
E.g.
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import axios from 'axios';
import { useEffect } from 'react';
export const fetchProject = createAsyncThunk('fetchProject', (id) => {
return axios.get('http://localhost:5000/api/project/' + id).then((res) => res.data);
});
export const projectSlice = createSlice({
name: 'project-redux',
initialState: {
name: '',
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchProject.fulfilled, (state, action) => {
state.name = action.payload;
});
},
});
export default projectSlice.reducer;
// Component
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
function Test() {
const dispatch = useDispatch();
const { name } = useSelector((state) => state.projs);
useEffect(() => {
dispatch(fetchProject('1'));
}, [dispatch]);
return <div>{name}</div>;
}
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 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;