the redux state didn t change after trying updating - reactjs

res in then gets me an object but still can t update it redux state
i created account the when it returns me the value res .i can console it but i can t save it in the redux state
const initialState = {
user: {},
};
export const Auth_Slice = createSlice({
name: "auth",
initialState,
reducers: {
loginWithEmail: async (state, action) => {
await createUserWithEmailAndPassword(
auth,
action.payload.email,
action.payload.password
).then((res) => {
state.user = res;
console.log(res.user);
});
},
},
});

you need to create async actions and use extraReducers in create slice.
export const createUserWithEmailAndPassword = createAsyncThunk(
"createUserWithEmailAndPassword",
async (payload) => {
const response = await createUserWithEmailAndPassword(
auth,
payload.email,
payload.password
);
return response;
}
);
const initialState = {
status: "",
user: {},
};
export const Auth_Slice = createSlice({
name: "auth",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(createUserWithEmailAndPassword.pending, (state) => {
state.status = "loading";
})
.addCase(createUserWithEmailAndPassword.fulfilled, (state, action) => {
state.status = "idle";
state.user = action.payload?.user ?? {};
});
}
})
;

Related

extraReducers builder.addCase rejected state not showing Using Redux Thunk With Redux Toolkit With Typescript

I am trying to make a POST request to an endpoint with React. On fulfilled state of my builder.addCase reducer, my data is generated or return back to me when fulfilled, but on rejected state of my builder.addCase reducer, checking my Redux Dev Tools, no rejected state is found even though I have an error. Rather the error message that ought to be in rejected state is found or populated to fulfilled state. Checking Redux Dev Tools I can only find pending and fulfilled state, rejected state is nowhere to be found.
Here is my code:
export const userRegisterAction = createAsyncThunk(
"users/register",
async (user: User, { rejectWithValue }) => {
try {
const response = await axios.post(
"http://localhost:5000/api/users/register",
user,
);
return response.data;
} catch (error) {
return rejectWithValue(error);
}
}
);
Here is my slice:
const usersSlices = createSlice({
name: "users",
initialState: {
userAuth: "login",
registered: {},
loading: false,
Error: ""
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(userRegisterAction.pending, (state) => {
state.loading = true
})
builder.addCase(userRegisterAction.fulfilled, (state, { payload }) => {
state.registered = payload
})
builder.addCase(userRegisterAction.rejected, (state) => {
state.loading = false;
})
}
});
export default usersSlices.reducer;
Here is where I dispatch my action.
const dispatch = useAppDispatch();
export interface User {
firstName : string;
password : string;
lastName: string;
email : string;
}
export const Signup = (): JSX.Element => {
const [passwordFieldType, setPasswordFieldType] = useState<boolean>(false);
const [user, setUser] = useState<User>({
firstName: "",
lastName: "",
email: "",
password: ""
});
const dispatch = useAppDispatch();
const handleInputChange = (e: any) => {
const name = e.target.name;
const value = e.target.value;
setUser({
...user,
[name]: value.trim()
});
}
const handleInputSubmit = async (event: any) => {
event.preventDefault();
const { firstName, lastName, email, password } = user;
if (!firstName || !lastName || !email || !password) {
return toast.error(
"Please, fill up all inputs !!!",
{
toastId: "fill_inputs",
position: toast.POSITION.TOP_CENTER,
autoClose: 1000,
}
);
}
const response = await dispatch(userRegisterAction({
firstName,
email,
password,
lastName
}))
}
}
I have tried everything I could, but to no avail. I've checked online too, no related help or answer to the issue.
I encountered the same challenge a few minutes ago. For me, it was because I did not add "return" before my "rejectWithValue"; which prevented my "rejected" from working because the function is a promise which has three states. And whenever you are working with a promise, always return your response.
For you, you need to parse "error.response.data" instead of only "error" in your reject function and update the state "Error" with the rejected action. See the implementation below.
The previous implementation of the userRegisterAction function
export const userRegisterAction = createAsyncThunk(
"users/register",
async (user: User, { rejectWithValue }) => {
try {
const response = await axios.post(
"http://localhost:5000/api/users/register",
user,
);
return response.data;
} catch (error) {
return rejectWithValue(error);
}
}
);
The new implementation of the userRegisterAction function
export const userRegisterAction = createAsyncThunk(
"users/register",
async (user: User, { rejectWithValue }) => {
try {
const response = await axios.post(
"http://localhost:5000/api/users/register",
user,
);
return response.data;
} catch (error) {
return rejectWithValue(error.response.data); //This carries the response you are receiving from the server
}
}
);
Previous implementation of the usersSlice
const usersSlices = createSlice({
name: "users",
initialState: {
userAuth: "login",
registered: {},
loading: false,
Error: ""
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(userRegisterAction.pending, (state) => {
state.loading = true
})
builder.addCase(userRegisterAction.fulfilled, (state, { payload }) => {
state.registered = payload
})
builder.addCase(userRegisterAction.rejected, (state) => {
state.loading = false;
})
}
});
export default usersSlices.reducer;
New implementation of usersSlice
const usersSlices = createSlice({
name: "users",
initialState: {
userAuth: "login",
registered: {},
loading: false,
Error: ""
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(userRegisterAction.pending, (state) => {
state.loading = true
})
builder.addCase(userRegisterAction.fulfilled, (state, { payload }) => {
state.registered = payload
})
builder.addCase(userRegisterAction.rejected, (state) => {
state.loading = false;
state.Error = payload; // Note: I updated the state here
})
}
});
export default usersSlices.reducer;
I hope this helps you resolve the bug.

Data fetching with Redux RTK - watching states

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

Redux toolkit update state in thunk

I have an initial state like this.
const initialState = {
loading: false,
products: []
}
For the get products, I use thunk and I fetch datas from an API. loading field describes status of API call. If loading is true, i show progress in my component.
export const getProducts = createAsyncThunk('product/getProducts ',
async (payload, { rejectWithValue }) => {
try {
//fetch from somewhere
}
catch (e) {
return rejectWithValue(e.message)
}
}
)
const productSlice = createSlice({
name: "product",
initialState,
reducers: {
setLoading: (state, action) => {
state.loading = action.payload;
}
},
})
In a component first dispatch for loading and then getProducts.
// In some component
dispatch(setLoading(true))
dispatch(getProducts())
My question is, can I only call for getProducts and update loading state inside that function?
//In some component
dispatch(getProducts())
Yes you can. This is very simple
Update your createSlice section like this
const productSlice = createSlice({
name: "product",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(getProducts.pending, (state) => {
state.loading = true;
});
builder.addCase(getProducts.fulfilled, (state) => {
state.loading = false;
});
},
})
You used createAsyncThunk and it is built in already to handle pending/fulfilled/rejected state with extraReducers in createSlice.
You can also handle the rejected state if fetch fail.
const productSlice = createSlice({
name: 'product',
initialState,
extraReducers: (builder) => {
builder.addCase(getProducts.pending, (state, action) => {
state.isLoading = true;
})
builder.addCase(getProducts.fulfilled, (state, action) => {
state.isLoading = true;
state.products = action.payload
})
builder.addCase(getProducts.rejected, (state, action) => {
state.isLoading = false;
})
},
})
To use it, will be enough to simple call dispatch(getProducts()) and extraReducers with thunk take care of the rest.

How to automatically refresh data with Redux Toolkit

I'm trying to achieve automated refetching without using RTK Query.
I.e. im writing my API calls using createAsyncThunk & Axios.
On create(post) or update(patch) request being successful, I would rather just "refetch" data, rather than push my changes into the original data set.
This is how it would be done with RTK Query:https://redux-toolkit.js.org/rtk-query/usage/automated-refetching
By invalidating the data.
A simple solution would be if I could call my "fetchClients" thunk..
Any ideas?
import { createSlice, createAsyncThunk, current } from "#reduxjs/toolkit/";
import axios from "axios";
import { getAxiosHeaders } from "../Api/apiHelper";
const initialState = {
clients: [],
client: null,
status: "idle", //'idle' | 'loading' | 'succeeded' | 'failed'
error: null,
isModalOpen: false,
};
export const fetchClients = createAsyncThunk("clients/fetchClients", async () => {
alert("hello fetch clients");
const response = await axios.get(CLIENTS_URL, { headers });
return response.data.clients;
});
export const addNewClient = createAsyncThunk("clients/addNewClient", async (body) => {
const response = await axios.post(CLIENTS_URL, body, { headers });
return response.data;
});
export const clientsSlice = createSlice({
name: "clients",
initialState,
reducers: {
toggleModal: (state, action) => {
state.isModalOpen = !action.payload;
},
},
},
extraReducers: (builder) => {
builder
.addCase(fetchClients.pending, (state, action) => {
state.status = "loading";
})
.addCase(fetchClients.fulfilled, (state, action) => {
state.status = "success";
state.clients = action.payload;
})
.addCase(fetchClients.rejected, (state, action) => {
state.status = "failed";
state.error = action.error.message;
})
.addCase(addNewClient.fulfilled, (state, action) => {
console.log(state.clients);
// REFETCH DATA i.e. fetchClients()
// state.clients.push(action.payload);
})
},
});
export const { clientAdded, toggleModal, setClient } = clientsSlice.actions;
export default clientsSlice.reducer;

redux-toolkit extraReducers not being invoked

I'm trying out the createAsyncThunk but can't get the extraReducers to fire. Here is what I have right now:
export const getAllAgents = createAsyncThunk(
"getAllAgents",
async (token: string) => {
let headers = new Headers();
headers.append("Authorization", `Bearer ${token}`);
headers.append("Content-Type", "application/json");
const response = await fetch(`${API.endpoint}/data/agent`, {
method: "get",
headers,
redirect: "follow",
});
const data = await response.json();
console.log("Response in thunk: ", { data });
return data;
}
);
export const agentSlice = createSlice({
name: "agentSlice",
initialState,
reducers: {},
extraReducers:
//builder call back required for typesafety
//https://redux-toolkit.js.org/usage/usage-with-typescript#type-safety-with-extrareducers
(builder) => {
console.log(createNewAgent.fulfilled);
builder.addCase(createNewAgent.fulfilled, (state, action) => {
console.log("fulfilled action", { action });
});
console.log(getAllAgents.fulfilled);
builder.addCase(getAllAgents.fulfilled, (state, action) => {
state.agents = action.payload.agents;
});
},
});
I also see the fulfilled action being called in the dev tools:
What is amiss?
I had the same problem. My mistake was that I was adding reducers outside of extraReducers.
This was wrong:
const authSlice = createSlice({
name: 'auth',
initialState,
extraReducers: {
[login.fulfilled]: (state, action) => {},
},
[signUp.fulfilled]: state => {}
}

Resources