I have an api which gives me the result, and I can see the data in my console, but I'm not able to get it in useSelector.
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import axios from "axios";
import { useNavigate } from "react-router-dom";
const initialState = {
value: [],
status: 'idle',
};
export const fetchEmployeesThunk = createAsyncThunk(
'employeelist/fetchEmployeesThunk',
async () => {
const res = await axios.get('https://localhost:7168/Employee/GetEmployeeList').then(
(result) => result.data
)
return res;
})
export const EmployeeListSlice = createSlice({
name: "employeelist",
initialState: initialState,
reducers: {
initialFetch: (state, action) => {
state.value = action.payload;
},
updateEmployeeList: (state, action) => {
state.value = action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(fetchEmployeesThunk.pending, (state, action) => {
state.status = 'idle';
state.value = [];
})
.addCase(fetchEmployeesThunk.fulfilled, (state, action) => {
console.log(action.payload);
state.value = action.payload;
state.status = 'finished';
})
},
});
export const getEmployeeListData = (state) => state.employeelist.value;
export const { updateEmployeeList, initialFetch } = EmployeeListSlice.actions;
export default EmployeeListSlice.reducer;
export function fetchEmployees() {
return async (dispatch) => {
const res = await axios.get('https://localhost:7168/Employee/GetEmployeeList').then(
(result) => result.data
)
dispatch(updateEmployeeList(res));
}
}
as you can see I tried using both thunk and creating a function and dispatching the data internally to an action, i was able to update the state but i'm not able to get the value through selector, I have a table which takes an array
export default function HomePage() {
const dispatch = useDispatch();
const [tempRows, setTempRows] = useState(useSelector((state) => state.employeelist.value));
const [rows, setTableRows] = useState(useSelector((state) => state.employeelist.value));
useEffect(() => {
//dispatch(fetchEmployees());
dispatch(fetchEmployeesThunk());
}, rows);
}
This is giving me empty array, but lets say if I change something then reload like a hot reload it returns the data now, any help would be deeply appreciated
Please do
const rows = useSelector((state) => state.employeelist.value)
and not
const [rows, setTableRows] = useState(useSelector((state) => state.employeelist.value));
The latter means "use local state that is once initialized from the Redux store". It will only change if setTableRows is called, not if the Redux store changes.
Related
I have a async action name editLoginIdData() in loginsIdSlice.js,
which i am dispatching from certain component, which edits the data in mongoDB database, then when the action is fullfilled, i mutate the state in extraReducers
editLoginIdData.fulfilled.
But now what i want to do that whenever the editLoginIdData action is fullfilled
i want to also add the updated(which i will get from server responese -> updatedData at editLoginIdData()) data to activitiesData state, which i am handling in activitiesSlice.js
So basically is there a way that when editLoginIdData action is fullfilled we can
dispatch somehow mutate the state in other slice.
One approach i have taken is to import the editLoginIdData() action in activitiesSlice.js and then creating a extraReducer with editLoginIdData.fullfilled
and mutating activitiesData state.
I have done the above approach and seems its working correctly.
But there is a catch, like how show i get the response data at editLoginIdData()
to passed to activitiesSlice.js because i will required that updated data.
if the above appraoch is not correct, then how should i do it
loginsIdSlice.js
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import * as api from "../../api"
const initialState = {
loginsIdData: [],
}
export const fecthLoginIdsData = createAsyncThunk("loginIds/fetch", async ({ user_id }, { getState }) => {
const res = await api.fetchUserLoginIds(user_id);
console.log(res);
const { data } = res;
data.reverse();
return data;
});
export const addNewLoginIdData = createAsyncThunk("loginIds/add", async ({ data, user_id }, { getState }) => {
const res = await api.addNewLoginIdA(data, user_id)
const { loginIdsArray } = res.data;
return loginIdsArray[loginIdsArray.length - 1];
});
export const editLoginIdData = createAsyncThunk("loginIds/edit", async ({ updatedData, login_id }, { getState }) => {
const res = await api.editLoginId(login_id, updatedData);
// console.log(updatedData);
return updatedData;
});
export const deleteLoginData = createAsyncThunk("loginIds/delete", async ({ login_id, user_id }, { getState }) => {
const res = await api.deleteLoginId(login_id, user_id);
// console.log(res);
const { data } = res;
// console.log(data);
return data.reverse();
});
//* Slice
const loginsIdSlice = createSlice({
name: 'loginsId',
initialState: initialState,
extraReducers: (builder) => {
builder.
addCase(fecthLoginIdsData.fulfilled, (state, action) => {
return {
...state,
loginsIdData: action.payload
};
}).
addCase(addNewLoginIdData.fulfilled, (state, action) => {
return {
...state,
loginsIdData: [action.payload, ...state.loginsIdData]
};
}).
addCase(editLoginIdData.fulfilled, (state, action) => {
const newArray = state.loginsIdData.map((loginId) => {
if (loginId._id === action.payload._id) {
return action.payload;
} else {
return loginId;
}
});
return {
...state,
loginsIdData: newArray,
};
}).
addCase(deleteLoginData.fulfilled, (state, action) => {
return {
...state,
loginsIdData: action.payload
};
})
}
})
export const { deleteLoginId, editLoginId } = loginsIdSlice.actions;
export default loginsIdSlice.reducer;
activitiesSlice
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import * as api from "../../api"
import { editLoginIdData } from "../loginsId/loginsIdSlice"
const initialState = {
activitiesData: [],
}
const activitiesSlice = createSlice({
name: 'activities',
initialState: initialState,
extraReducers: (builder) => {
builder.
addCase(editLoginIdData.fullfilled, (state, action) => {
console.log("ss")
return {
...state,
activitiesData: []
};
})
}
})
export default activitiesSlice.reducer;
Is there a way that when editLoginIdData action is fullfilled we can
dispatch somehow mutate the state in other slice.
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 am using redux toolkit with thunk to receive data from api.
I need to fetch data from 2 apis in consecutive order using data I got from the first api call as a argument of second api call (search1 first, then search2)
In order to do that, I need to wait for the first dispatch to fully complete its job from calling getSearch1 to updating the state.
Please help!
// store
import { configureStore } from "#reduxjs/toolkit";
import searchReducer from "./slice/searchSlice";
export const store = configureStore({
reducer: {
search: searchReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
// slice
export const getSearch1 = createAsyncThunk(
"search/getSearch1",
async (args: string[]) => {
const result = await ApiUtil.search1(args);
return result;
}
);
export const getSearch2 = createAsyncThunk(
"search/getSearch2",
async (ids: string[]) => {
const result = await ApiUtil.search2(ids);
return result;
}
);
export const searchSlice = createSlice({
name: "search",
initialState,
reducers: {...},
extraReducers: (builder) => {
builder
.addCase(getSearch1.fulfilled, (state, action) => {
state.search1 = action.payload;
})
.addCase(getSearch2.fulfilled, (state, action) => {
state.search2 = action.payload;
});
},
});
// home page
import {
...
getSearch1,
getSearch2,
} from "../../redux/slice/searchSlice";
const handleSearch = () => {
dispatch(getSearch1(args));
const ids = search1?.map((item) => item.id.toString());
dispatch(getSearch2(ids ?? []));
history.push(ROUTES.RESULT_PAGE, search1);
};
You can use .unwrap() method to achieve that,see the documentation
:
try {
const { data } = await dispatch(getSearch1(args)).unwrap()
await dispatch(getSearch2(data ?? []));
// handle result here
} catch (rejectedValueOrSerializedError) {
// handle error here
}
I solved it just as slideshowp2's shared link.
useEffect(() => {
getResult();
}, [dispatch]); // listen for dispatch(), should run getResult() twice
const getResult = async () => {
let action;
if (!search1) { // skip below when called twice
action = await dispatch(getSearch1(args));
}
if (isFulfilled(action)) {
const id = action.payload.map((item) => item.id.toString());
dispatch(getSearch2(id ?? []));
}
};
I am working on a project and I need to fetch data from backend or from an API. I tried fetch the data but nothing appears. I think I am doing something wrong in the container. I am a beginner in react-redux, I don't know what I am doing wrong.
I've already read all the posts but nothing seems to works.
my reducer:
const initialState={
articles: [],
};
const rootReducer = (state = initialState, action) => {
const { type, payload }=action;
switch(type) {
case SRETRIEVE_ARTICLE:{
return {
...state,
articles:payload,
};
}
default: return state;
}
}
export default rootReducer;
This is what I have right now in container:
import Articles from 'components/Articles';
import { fetchArticles } from '../../pages/index';
const mapStateToProps = (state) => ({
articles:state.articles
})
const ConnectedArticles = connect(
mapStateToProps,
{fetchArticles}
)(Articles)
export default ConnectedArticles;
pages.js
axios.get('API').then((response) => {
const { data } = response;
dispatch({ type: RETRIEVE_ARTICLES, payload: data });
});
};
const Index = () => {
const articles= useSelector((state) => state.articles);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchArticles);
}, []);
return <>{articles && articles.map((article) => <Article key={article.id} name={article.name} />)}</>;
};
Index.getInitialProps = async () => ({
authRequired: true,
label: 'Dashboard',
});
export default Index;
Also I defined the action type: export const SET_UNOPENED_REWARD = 'SET_UNOPENED_REWARD';
and action const unopenedRewards = (payload) => ({ type: SET_UNOPENED_REWARD, payload });
One very nice way to do data fetching with redux is to use redux toolkit's createAsyncThunk and createSlice functions.
// src/features/articles/articlesSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
export const fetchArticles = createAsyncThunk("articles/get", async () => {
// Here you can use axios with your own api
const response = await fetch("https://rickandmortyapi.com/api/character");
const json = await response.json();
return json.results;
});
export const slice = createSlice({
name: "articles",
initialState: {
loading: false,
data: []
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchArticles.pending, (state) => {
state.loading = true;
});
builder.addCase(fetchArticles.fulfilled, (state, action) => {
state.data = action.payload;
state.loading = false;
});
builder.addCase(fetchArticles.rejected, (state) => {
state.loading = false;
});
}
});
export default slice.reducer;
// src/features/articles/Articles.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { fetchArticles } from "./articlesSlice";
export const Articles = () => {
const articles = useSelector((state) => state.articles.data);
const loading = useSelector((state) => state.articles.loading);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchArticles());
}, []);
return (
<>
{loading && "...loading"}
{articles.map((article) => <Article key={article.id} {...article} />)}
</>
);
};
you should use async and await
let response = await axios.get('https://run.mocky.io/v3/5c045896-3d18-4c71-a4e5-5ed32fbbe2de')
if(response.status==200){
dispatch({ type: RETRIEVE_ARTICLES, payload: data });
}
Please help me how I can introduce new function like getOrdersByCustomer in ordersSlice. I have provided full code of ordersSlice below. Please tell me what is extraReducers and how it works.
import { createSlice, createAsyncThunk, createEntityAdapter } from '#reduxjs/toolkit';
import axios from 'axios';
export const getOrders = createAsyncThunk('eCommerceApp/orders/getOrders', async () => {
const response = await axios.get('/api/e-commerce-app/orders');
const data = await response.data;
return data;
});
export const removeOrders = createAsyncThunk(
'eCommerceApp/orders/removeOrders',
async (orderIds, { dispatch, getState }) => {
await axios.post('/api/e-commerce-app/remove-orders', { orderIds });
return orderIds;
}
);
const ordersAdapter = createEntityAdapter({});
export const { selectAll: selectOrders, selectById: selectOrderById } = ordersAdapter.getSelectors(
state => state.eCommerceApp.orders
);
const ordersSlice = createSlice({
name: 'eCommerceApp/orders',
initialState: ordersAdapter.getInitialState({
searchText: ''
}),
reducers: {
setOrdersSearchText: {
reducer: (state, action) => {
state.searchText = action.payload;
},
prepare: event => ({ payload: event.target.value || '' })
}
},
extraReducers: {
[getOrders.fulfilled]: ordersAdapter.setAll,
[removeOrders.fulfilled]: (state, action) => ordersAdapter.removeMany(state, action.payload)
}
});
export const { setOrdersSearchText } = ordersSlice.actions;
export default ordersSlice.reducer;
In Addition
Also can you please tell me what I will do with this following code for my custom function getOrdersByCustomer.
export const { selectAll: selectOrders, selectById: selectOrderById } = ordersAdapter.getSelectors(
state => state.eCommerceApp.orders
);
because, in my component I have used like
const orders = useSelector(selectOrders);
You can introduce new (async) functions as you already have (I used the customerId as part of the url -> you could access it through the params in your backend):
export const getOrdersByCustomer = createAsyncThunk('eCommerceApp/orders/getOrdersByCustomer', async (customerId) => {
const response = await axios.get(`/api/e-commerce-app/orders/${customerId}`);
const data = await response.data;
return data;
});
Then you can handle the response in your extraReducer:
extraReducers: {
[getOrders.fulfilled]: ordersAdapter.setAll,
[removeOrders.fulfilled]: (state, action) => ordersAdapter.removeMany(state, action.payload),
[getOrdersByCustomer.fulfilled]: (state, action) =>
// set your state to action.payload
}
The extraReducers handle actions like async thunks. The createAsyncThunk function return 3 possible states (along with other things): pending, rejected or fulfilled. In your case you only handle the fulfilled response. You could also set your state with the other two options (in your case [getOrdersByCustomer.pending] or [getOrdersByCustomer.rejected]