I'm trying to use "#reduxjs/toolkit"
So I have a page: "services.reducer.ts":
const initialState = {
entities: [] as ReadonlyArray<IServices>,
entity: defaultValue,
loading: false,
totalItems: 0
}
export type ServiceState = Readonly<typeof initialState>;
export const getEntities = createAsyncThunk(
'services/getEntities',
async () => {
const response = await axios.get<any>(apiUrl)
return response.data
}
)
export const getEntity = createAsyncThunk(
'services/getEntity',
async (id: number | string) => {
const response = await axios.get<any>(`${apiUrl}/${id}`);
return response.data
}
)
export const ServiceSlice = createSlice({
name: 'services',
initialState,
reducers: {},
extraReducers(builder) {
builder
.addCase(getEntities.pending, (state, action) => {
state.loading = false;
})
.addCase(getEntities.fulfilled, (state, action) => {
const { data } = action.payload;
state.entities = data;
state.loading = true;
state.totalItems = parseInt(action.payload.length, 10);
})
.addCase(getEntity.fulfilled, (state, action) => {
const { data } = action.payload;
state.entity = data;
})
}
})
on my services.tsx:
export const Services = (props: IServicesProps) => {
const dispatch = useAppDispatch();
const [list, setList] = useState([]);
const [loading, setLoading] = useState(false)
useEffect(() => {
dispatch(getEntities())
.then((resp) => {
console.log("resp ", resp)
if (resp.payload !== null) {
setLista(resp.payload)
}
})
}, []);
Now my question is: I would to use the info about loading and totalItems for example. But I don't understand how can I use in on my services.tsx
I have, for example, set:
state.loading = true;
state.totalItems = parseInt(action.payload.length, 10);
but I don't understand if they are declared in the right place or not.
EDIT:
store.ts
import { AnyAction, configureStore, ThunkAction } from '#reduxjs/toolkit';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import reducer from 'app/shared/reducers';
import errorMiddleware from './error-middleware';
import notificationMiddleware from './notification-middleware';
import loggerMiddleware from './logger-middleware';
import { loadingBarMiddleware } from 'react-redux-loading-bar';
const store = configureStore({
reducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
}).concat(errorMiddleware, notificationMiddleware, loadingBarMiddleware(), loggerMiddleware),
})
const getStore = () => store;
export type IRootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export const useAppSelector: TypedUseSelectorHook<IRootState> = useSelector;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, IRootState, unknown, AnyAction>;
export default getStore;
EDIT 2:
import { loadingBarReducer as loadingBar } from 'react-redux-loading-bar';
import { combineReducers } from 'redux';
import authentication, { AuthenticationState } from './authentication';
import applicationProfile, { ApplicationProfileState } from './application-profile';
import administration, { AdministrationState } from 'app/modules/administration/administration.reducer';
import userManagement, { UserManagementState } from './user-management';
// prettier-ignore
import services, {
ServiceState
} from 'app/entities/service/service.reducer';
// prettier-ignore
/* jhipster-needle-add-reducer-import - JHipster will add reducer here */
export interface IRootState {
readonly authentication: AuthenticationState;
readonly applicationProfile: ApplicationProfileState;
readonly administration: AdministrationState;
readonly userManagement: UserManagementState;
readonly services: ServicesState;
readonly loadingBar: any;
}
const rootReducer = combineReducers<IRootState>({
authentication,
applicationProfile,
administration,
userManagement,
services,
,
/* jhipster-needle-add-reducer-combine - JHipster will add reducer here */
loadingBar,
});
export default rootReducer;
EDIT 3: console.log("resp", resp):
{
"type": "services/getEntities/fulfilled",
"payload": [
{
"srvCod": 1,
"srvDescod": "Account Savings",
"srvDescodmaia": "Corporate Islan",
"srvDestitle": "Oklahoma",
},
],
"meta": {
"arg": {
"sortParam": "srvCod,asc",
"pageParam": 0
},
"requestId": "OTD4JuHBUeYF7ueyg6BQ2",
"requestStatus": "fulfilled"
}
}
Related
Slice One
/*eslint-disable*/
import { createAsyncThunk, createSlice, PayloadAction } from '#reduxjs/toolkit';
const initialState = { };
const patientSlice = createSlice({
name: 'Patient Slice',
initialState: initialState,
reducers: {
TestingOtherSliceCall: (state: any, { payload }: any) => {
alert('heelllo im from patient slice');
}
},
extraReducers: {
[GetInsurancePayersFilter.pending]: (state: any) => {
state.loadingInsurancePayersFilter = true;
},
[GetInsurancePayersFilter.fulfilled]: (state: any, { payload }: PayloadAction<TPayloadList>) => {
state.loadingInsurancePayersFilter = false;
state.getInsurancePayersFilter = payload;
},
[GetInsurancePayersFilter.rejected]: (state: any) => {
state.loadingInsurancePayersFilter = false;
},
}
});
export const { TestingOtherSliceCall patientSlice.actions;
export const patientReducer = patientSlice.reducer;
Slice Two
/*eslint-disable*/
import { createAsyncThunk, createSlice, PayloadAction } from '#reduxjs/toolkit';
import { TestingOtherSliceCall } from './patient';
const initialState = {};
insurance end ******** */
const userSlice = createSlice({
name: 'User Slice',
initialState: initialState,
reducers: {
DynamicTabAppend: (state: any, { payload }) => {
TestingOtherSliceCall(null);
},
},
extraReducers: {
}
});
export const { TestingOtherSliceCall userSlice.actions;
export const userReducer = userSlice.reducer;
Root Reducer
import { patientReducer } from './../slices/patient';
import { combineReducers } from '#reduxjs/toolkit';
import storage from 'redux-persist/lib/storage';
import { persistReducer } from 'redux-persist';
import { createFilter } from 'redux-persist-transform-filter';
export const rootReducer = combineReducers({
user: userReducer,
patient: patientReducer,
});
export const persistConfig = {
key: 'root',
storage,
whitelist: ['auth', 'schedule'],
};
export const persistedReducer = persistReducer(persistConfig, rootReducer);
I tried calling from one slice reducer to another but function does'nt invoke.
Am trying to make a login and add the return data to state, when I try to update state, it still gets empty state. I have added the code below, Please help me fix it. Am using RTK query to make the login mutation, and am new to this.
My AuthApi.js
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import { api } from "./api";
export const authApi = createApi({
reducerPath: "authApi",
baseQuery: fetchBaseQuery({
baseUrl: api
}),
endpoints: (builder) => ({
loginUser: builder.mutation({
query: (body) => {
return {
url: "login",
method: "post",
body,
};
},
}),
registerUser: builder.mutation({
query: (body) => {
return {
url: "register",
method: "post",
body,
};
},
}),
}),
});
export const { useLoginUserMutation, useRegisterUserMutation } = authApi;
My userSlice.js
import { createSlice } from "#reduxjs/toolkit";
import { authApi } from "../../services/authApi";
const initialState = {
role: null,
accessToken: null,
userData: null
}
export const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
logout: (state) => {
state.currentUser = null;
state.errorMessage = ''
}
},
extraReducers: (builders) => {
builders.addMatcher(authApi.endpoints.loginUser.matchFulfilled, (state, action) => {
state.userData = action.payload.success.data
state.accessToken = action.payload.success.accessToken
state.role = action.payload.success.roles
})
}
})
export const { logout } = userSlice.actions
export const selectUser = (state) => state.user.accessToken
export default userSlice.reducer
my store.js
import { configureStore } from "#reduxjs/toolkit";
import { authApi } from "../services/authApi";
import userReducer from './slices/userSlice'
const store = configureStore({
reducer: {
[authApi.reducerPath]: authApi.reducer,
user: userReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(authApi.middleware),
});
export default store;
console.log(action.payload) in userSlice extraReducer gives me the data in console, but it is not updating the state.
I don't get why dispatch is not sendin list: [] the information, only info I got is list: [] has it's property in never[].
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
export const userSlice = createSlice({
name: "users",
initialState: {
list: [] /* (property) list: never[] */
},
reducers: {
setUserList: (state, action) => {
state.list = action.payload;
}
},
});
export const { setUserList } = userSlice.actions;
export default userSlice.reducer;
export const fetchAllUsers = () => (dispatch) => {
axios
.get('https://reqres.in/api/users?page=2')
.then((response) => {
dispatch(setUserList(response.data.data));
})
.catch((error) => console.log(error))
};
I have just completed Learn Redux on Codecademy and want to that knowledge in practice. But I have an error. When I create extraReducers for updating the state to actual promise status it does not add information.
getUserSlice.js
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import { fetchUserInfo } from '../../api';
export const loadUser = createAsyncThunk("getUser/loadUser",
async (arg, thunkAPI) => {
return await fetchUserInfo();
}
});
const sliceOptions = {
name: 'getUser',
initialState: {
info: [],
isLoading: false,
hasError: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(loadUser.pending, (state) => {
state.isLoading = true;
state.hasError = false;
})
.addCase(loadUser.fulfilled, (state, action) => {
state.info.push(action.payload)
state.isLoading = false;
state.hasError = false;
})
.addCase(loadUser.rejected, (state, action) => {
state.isLoading = false;
state.hasError = true;
})
},
};
export const getUserSlice = createSlice(sliceOptions);
console.log(getUserSlice);
export const selectUserInfo = (state) => {
console.log(state);
return state;
};
export default getUserSlice.reducer;
api.js
export const fetchUserInfo = async () => {
const user = await fetch('http://localhost:5000/api/user');
const json = user.json();
return json;
}
App.js
import React from 'react';
import './App.css';
import {Container} from 'react-bootstrap';
import Achievement from './components/Achievement/Achievement';
import { useSelector } from 'react-redux';
import { selectUserInfo } from './features/getUser/getUserSlice';
const colors = ['#010626','#4d6396', '#5d1a87', '#5d1a87', '#5d1a87'];
function App() {
let color= colors[0];
const user = useSelector(selectUserInfo)
function changeColor() {
const newColor = `rgb(${Math.round(Math.random() *256)}, ${Math.round(Math.random() *256)}, ${Math.round(Math.random() *256)})`;
color = newColor;
}
return (
<div className="App" style={{ background: color }}>
<Container>
<h1 id="whoAmI">
Witaj na moim portfolio
{user}
</h1>
<button onClick={changeColor}>
Zmień kolor tła
</button>
<div className="col-lg-4 col-md-6 col-sm-12">
<Achievement title="Ukończenie The Web Developer Bootcamp" picture="https://res.cloudinary.com/syberiancats/image/upload/v1630317595/k9g0nox2fyexxawg8whu.jpg" />
</div>
</Container>
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import reportWebVitals from './reportWebVitals';
import { store } from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
reportWebVitals();
store.js
import { configureStore } from "#reduxjs/toolkit";
import getUserReducer from "./features/getUser/getUserSlice";
export const store = configureStore({
reducer: {
getUser: getUserReducer
}
})
Console.log of getUserSlice and state in the selector
Maybe you can use (builder) => {} function in extraReducer and you edit your loadUser because your Api.js already return json like code below:
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import { fetchUserInfo } from '../../api';
export const loadUser = createAsyncThunk('getUser/loadUser', async (arg, thunkAPI) => {
return await fetchUserInfo();
});
const sliceOptions = {
name: 'getUser',
initialState: {
info: [],
isLoading: false,
hasError: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(loadUser.pending, (state) => {
state.isLoading = true;
state.hasError = false;
})
.addCase(loadUser.fulfilled, (state, action) => {
state.info.push(action.payload)
state.isLoading = false;
state.hasError = false;
})
.addCase(loadUser.rejected, (state, action) => {
state.isLoading = false;
state.hasError = true;
})
},
};
export const getUserSlice = createSlice(sliceOptions);
console.log(getUserSlice);
export const selectUserInfo = (state) => {
console.log(state);
return state;
};
export default getUserSlice.reducer;
and you maybe forgot to add await before fetch, you should edit your fetching data in api.js into this below:
export const fetchUserInfo = async () => {
const user = await fetch('http://localhost:5000/api/user');
const json = user.json();
return json;
}
You can implicitly return and bypass the Promise result. Something like:
(arg, thunkAPI) => fetchUserInfo();
However, I would take the "by-the-book" way:
export const loadUser = createAsyncThunk("getUser/loadUser",
async (arg, tunkApi) => {
try {
const response = await fetchUserInfo();
return response;
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}
});
I created a redux store with 2 different "Slice". First, I have an appSlice:
appSlice.ts
import { createSlice, PayloadAction } from "#reduxjs/toolkit";
import type { RootState } from "./store";
export interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0,
};
export const appSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = appSlice.actions;
export const selectCount = (state: RootState) => state.app.value;
export default appSlice.reducer;
And I have a cameraSlice.ts
import { createSlice, PayloadAction } from "#reduxjs/toolkit";
import type { RootState } from "./store";
export interface CameraState {
cameraImage: string | null;
}
const initialState: CameraState = {
cameraImage: null,
};
export const cameraSlice = createSlice({
name: "camera",
initialState,
reducers: {
setCameraImage: (state, action: PayloadAction<string>) => {
state.cameraImage = action.payload;
},
resetCameraImage: (state) =>{
state.cameraImage=null;
}
},
});
export const { setCameraImage, resetCameraImage } = cameraSlice.actions;
export const selectCameraImage = (state: RootState) => state.camera.cameraImage;
export default cameraSlice.reducer;
The error coming from this selectCameraImage, the error message: Property 'cameraImage' does not exist on type 'CounterState'.
Now my store looks like this:
store.ts
import { configureStore } from "#reduxjs/toolkit";
import appReducer from "./appSlice";
import cameraReducer from "./appSlice";
export const store = configureStore({
reducer: {
app: appReducer,
camera: cameraReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
hooks.ts:
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
How can I fix that issue? Now, the app and the camera are CounterState type, I don't know why.
If you have any idea, please let me know! Thanks for your time!
I don't see a CounterState defined anywhere there, so maybe you just have another file flying around?
Also, if you take a look at your imports,
import appReducer from "./appSlice";
import cameraReducer from "./appSlice";
is definitely wrong.