I have separated slices for every service from backend to control different states like loading and error etc., but don't know if there is better way to structure slices.
File structure for now looks like this:
featureName/service1Slice;
featureName/service2Slice;
featureName/service3Slice;
feature2Name/service1Slice;
etc...
And slice itself:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
import { IContract} from 'contracts';
import { ServiceName } from 'services';
interface IAllStatuses {
loading: boolean;
whoseDataIsLoading?: string;
error: boolean;
errorText?: string | unknown;
}
const initialState: IAllStatuses = {
loading: false,
whoseDataIsLoading: '',
error: false,
errorText: '',
};
export const serviceName = createAsyncThunk('servicename', async (request: IContract) => {
const response = await ServiceName(request);
return response;
});
export const serviceNameSlice = createSlice({
name: 'service-name',
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(serviceName.fulfilled, (state) => {
state.loading = false;
});
builder.addCase(serviceName.pending, (state, action) => {
state.loading = true;
state.whoseDataIsLoading = action.payload;
});
builder.addCase(serviceName.rejected, (state, action) => {
state.error = true;
state.loading = false;
state.errorText = action.payload;
});
},
});
export const serviceNameReducer= serviceNameSlice.reducer;
Related
I am fetching journeys base on search criteria. I want to store those criteria in store.
I tied to access action.payload in extrareducer pending but it is not working. May I know how to store the criteria param into store.
import { createSlice, createAsyncThunk, PayloadAction } from '#reduxjs/toolkit'
import axios from "axios"
import { Journey, SearchCriteria } from '../types'
import constants from "../constants";
type InitialState = {
loading: boolean
journeys: Journey[]
criteria: SearchCriteria | null
error: string
}
const initialState: InitialState = {
loading: false,
journeys: [],
criteria: null,
error: '',
};
// generates pending, fulfilled and rejected action types
export const fetchJourneys = createAsyncThunk("user/fetchUsers", async (criteria: SearchCriteria) => {
const response = await axios.get(`${constants.api_server}/journeys?from=${criteria.from}&to=${criteria.to}&departure=${criteria.departure}`);
return response.data.journeys;
});
const journeySlice = createSlice({
name: "journey",
initialState,
reducers: {},
extraReducers: builder => {
builder.addCase(fetchJourneys.pending, (state, action) => {
console.log(action)
state.loading = true
})
builder.addCase(fetchJourneys.fulfilled, (state, action: PayloadAction<Journey[]>) => {
state.loading = false
state.journeys = action.payload
state.error = ''
})
builder.addCase(fetchJourneys.rejected, (state, action) => {
state.loading = false,
state.journeys = [],
state.error = action.error.message || 'Someting went wrong'
})
}
});
export default journeySlice.reducer
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;
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 am using redux toolkit and firebase firestore for backend. I just want to get an array of objects from the database. Below is the code for slice. When I log the payload in the console I am unable to get the data. Thanks in advance.
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import firestore from '#react-native-firebase/firestore';
export const getCarouselImages = createAsyncThunk("/carouselImages", () =>
firestore().collection('Users').get()
);
const initialState = {
isLoading: false,
failed: true,
success: false,
imageArray:[],
};
const carouselData = createSlice({
name: "carouselImageSlice",
initialState,
reducers: {
resetCarouselData: () => initialState,
},
extraReducers: (builder) => {
builder.addCase(getCarouselImages.fulfilled, (state, { payload }) => {
state.isLoading = false;
state.failed = false;
state.success = true;
payload.forEach(doc => {
state.userData.push(doc.data())
});
console.log(payload)
});
builder.addCase(getCarouselImages.rejected, (state, action) => {
state.isLoading = false;
state.failed = true;
state.success = false;
});
builder.addCase(getCarouselImages.pending, (state, { payload }) => {
state.isLoading = true;
state.failed = false;
state.success = false;
});
},
});
export const { resetCarouselData } = carouselData.actions;
export default carouselData.reducer;
I'm working on an app where I have multiple slices. I'm using createAsyncThunk for API calls and I like it cause it provides action creators for different state of API request, so that I can track loading state and errors within the reducer. But my question is, what if I want to have a separate reducer to track loading, error and success of my API calls how do I accomplish that with redux-toolkit
I know I can dispatch an action from within my createAsyncThunk function but it doesn't feel right and kinda defeats the purpose of the function itself. Also, side effects inside the reducer are considered to be a bad practice. So, I'm kinda confused at this point, I want to have just one Loader component in the root of the app that gets triggered when the loading state is true and it doesn't matter what exactly is loading
Here is an example of my current code:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit'
import { AxiosError } from 'axios'
import { masterInstance } from 'api'
import { GetAccessCodeParams, RegistrationStateType } from 'store/slices/registration/types'
export const getAccessCodeRequest = createAsyncThunk<void, GetAccessCodeParams, { rejectValue: { message: string } }>(
'registration/getAccessCodeRequest',
async ({ email }, { rejectWithValue }) => {
try {
await masterInstance.post(`/authorization/getAccessCodeWc`, { email })
} catch (err) {
let error: AxiosError = err
if (error) {
return rejectWithValue({
message: `Error. Error code ${error.response?.status}`,
})
}
throw err
}
}
)
const initialState: RegistrationStateType = {
isLoading: false,
error: null,
}
const registrationSlice = createSlice({
name: 'registration',
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(getAccessCodeRequest.fulfilled, (state) => {
state.isLoading = false
state.error = null
})
builder.addCase(getAccessCodeRequest.pending, (state) => {
state.isLoading = true
state.error = null
})
builder.addCase(getAccessCodeRequest.rejected, (state, action) => {
if (action.payload) {
state.error = {
message: action.payload.message,
}
} else {
state.error = action.error
}
state.isLoading = false
})
},
})
export const registrationReducer = registrationSlice.reducer
I want isLoading and error to be in a separate reducer
You could have a shared reducer matcher function.
// mySharedStuff.js
export const handleLoading = (action, (state) => {
state.loading = action.type.endsWith('/pending'); // or smth similar
});
export const handleError = (action, (state) => {
state.error = action.type.endsWith('/rejected'); // or smth similar
});
// mySlice.js
const mySlice = createSlice({
name: 'FOO',
initialState: {},
reducers: {},
extraReducers: builder => {
builder.addMatcher(handleLoading),
builder.addMatcher(handleError),
...