'Passing data form parent to Child with the value fetch form API
I wanna passing the selectedCategory (it is State hook) to the Child Item, First of all, I use the getServiceCatoriesAsync API (redux toolkit) and pass props.serviceCategories[0]?._id to State to fetch initialState (ID of Category).
In Child Component, I receive selectedCategory with the value: undefined
How to fix this.
const ServicesScreen = (props) => {
//! props: navigation, route,
//! props Redux: serviceCategories, getServiceCategoriesAsync
const nCount = React.useRef(0);
console.log(`ServicesScreen - render `, (nCount.current += 1));
const [selectedCategory, setSelectedCategory] = React.useState(props.serviceCategories[0]?._id);
React.useEffect(() => {
let isSubscribed = true;
if (isSubscribed) {
props.getServiceCategoriesAsync();
}
return () => {
isSubscribed = false; //! Cancel the subscription
};
}, [selectedCategory]);
return (
<View style={styles.container}>
<PanelServiceCategory
theme={theme}
style={styles.containerPanelCategory}
setSelectedCategory={setSelectedCategory}
selectedCategory={selectedCategory}
serviceCategories={props.serviceCategories}
{...props}
/>
<PanelServices style={styles.containerPanelService} />
</View>
);
};
servicesSlice
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { PlatformBaseUrl } from '../../../utils';
//! GET ServiceCategory
export const getServiceCategoriesAsync = createAsyncThunk('services/getServiceCategoriesAsync', async () => {
const response = await fetch(PlatformBaseUrl.baseApiUrl('/api/services'));
if (response.ok) {
const { serviceCategories } = await response.json();
return serviceCategories; // payload Action
}
});
//! CREATE ServiceCategory
export const addServiceCategoryAsync = createAsyncThunk('services/addServiceCategoryAsync', async (payload) => {
const response = await fetch(PlatformBaseUrl.baseApiUrl('/api/services'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: payload.name }),
});
if (response.ok) {
const { serviceCategory } = await response.json();
return serviceCategory; //! return Action 1 Array
}
});
//! CREATE Service
export const addServiceAsync = createAsyncThunk('services/addServiceAsync', async (payload, { getState }) => {
const { serviceCategoryId } = getState().modal.modalProps; //! OK
const response = await fetch(PlatformBaseUrl.baseApiUrl(`/api/services/${serviceCategoryId}`), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: payload.name, price: payload.price, description: payload.description }),
});
if (response.ok) {
const { service } = await response.json();
return service;
}
});
//! DELETE Service
export const removeServiceAsync = createAsyncThunk('services/removeServiceAsync', async (payload, { getState }) => {
const { serviceCategoryId, serviceId } = getState().modal.modalProps;
const response = await fetch(PlatformBaseUrl.baseApiUrl(`/api/services/${serviceCategoryId}/${serviceId}`), {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ serviceId }),
});
if (response.ok) {
const { service } = await response.json();
return service;
}
});
//! UPDATE Service
export const updateServiceAsync = createAsyncThunk('services/updateServiceAsync', async (payload, { getState }) => {
const { serviceCategoryId, serviceId } = getState().modal.modalProps;
const { name, price, description } = payload;
const response = await fetch(PlatformBaseUrl.baseApiUrl(`/api/services/${serviceCategoryId}/${serviceId}`), {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, price, description }),
});
if (response.ok) {
const { updatedService } = await response.json();
return updatedService; //! return a Object
}
});
const initialState = {
isLoading: false,
error: false,
serviceCategories: [],
};
const servicesSlice = createSlice({
name: 'services',
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(getServiceCategoriesAsync.pending, (state, action) => {
console.log('getServiceCategoriesAsync pending');
});
builder.addCase(getServiceCategoriesAsync.fulfilled, (state, action) => {
console.log('getServiceCategoriesAsync fulfilled');
state.serviceCategories = action.payload;
});
builder.addCase(addServiceCategoryAsync.fulfilled, (state, action) => {
console.log('addServiceCategoryAsync fulfilled');
state.serviceCategories.push(action.payload);
});
builder.addCase(addServiceAsync.pending, (state, action) => {
console.log('addServiceAsync pending');
});
builder.addCase(addServiceAsync.fulfilled, (state, action) => {
console.log('addServiceAsync fulfilled');
let categories = [...state.serviceCategories];
let catIndex = categories.findIndex((item) => item._id === action.payload.category);
if (catIndex != -1) categories[catIndex].services.push(action.payload);
state.serviceCategories = categories;
});
builder.addCase(removeServiceAsync.pending, (state, action) => {
console.log('removeServiceAsync pending');
});
builder.addCase(removeServiceAsync.fulfilled, (state, action) => {
console.log('removeServiceAsync fulfilled');
let categories = state.serviceCategories;
let catIndex = categories.findIndex((item) => item._id === action.payload.category);
let updatedServices = categories[catIndex].services.filter((service) => service._id !== action.payload._id);
if (catIndex != -1) state.serviceCategories[catIndex].services = updatedServices;
});
builder.addCase(updateServiceAsync.pending, (state, action) => {
console.log('updateServiceAsync pending');
});
builder.addCase(updateServiceAsync.fulfilled, (state, action) => {
console.log('updateServiceAsync fulfilled');
let categories = state.serviceCategories;
let catIndex = categories.findIndex((item) => item._id === action.payload.category);
let updatedServices = categories[catIndex].services.map((service) => (service._id === action.payload._id ? action.payload : service));
if (catIndex != -1) state.serviceCategories[catIndex].services = updatedServices;
});
},
});
//! exp Actions
export const {} = servicesSlice.actions;
//! exp Reducer
export default servicesSlice.reducer;
Solution 1:[1]
I wish I could comment this under your post but my rep is too low so oh well.
The problem may be caused due to several reasons. To debug you need to show the parent file which gives props.serviceCategories[0]?._id to ServicesScreen. And also show how it calls the redux store to gain access to said data.
Also show the slice that handles the state for serviceCategories. It might be the case that you are mutating the state and hence the store is not causing a re-render.
EDIT
Alright so basically you are mutating some states that Immer cannot handle in redux-toolkit. the cases are wherever this has been done:
state.serviceCategories[catIndex].services = updatedServices;
According to the docs arrays are mutable in nature and changing them in such fashion means Immer cannot apply a copy to the state change (Although it is able to do so inside the createReducer() method). Therefore a better approach would be:
// inside updateServiceAsync.fulfilled and removeServiceAsync.fulfilled
let elementForInsertion = {...state.serviceCategories[catIndex], services: updatedServices}
if (catIndex != -1) state.seviceCategories = [...state.serviceCategories.slice(0,catIndex), elementForInsertion, ...state.serviceCategories.slice(catIndex+1)]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
