Reputation: 404
I'm trying to pass an object containing array to a child component
I've implemented some loading until my state array called profileModule
is populated
However, the object 'profileModule' being passed contains an empty array
Can anyone enlighten me on what am I missing? Please see code below.
Actual passing of object to child component:
var [state, dispatch] = useReducer(layoutReducer, {
isSidebarOpened: true,
modules: profileModules,
});
if (profileModules && profileModules.length > 0) {
return (
<LayoutStateContext.Provider value={state}>
<LayoutDispatchContext.Provider value={dispatch}>
{children}
</LayoutDispatchContext.Provider>
</LayoutStateContext.Provider>
);
} else {
return <LoadingOverlay active={true} spinner={<FadeLoader />} text="" />;
}
As you can see above, in useReducer
function, I'm trying to pass modules: profileModules
to a child component
I don't understand why I'm getting value when I console.log it before the actual rendering of page but when its inside useReducer
I only get empty array
Here is how I populate the value of profileModule
which comes from an API
useLayoutEffect(() => {
retrieveProfileDetails();
}, []);
useLayoutEffect(() => {
if (profileDetails.length > 0) {
const modules = profileDetails.map((module) => module.module);
setProfileModules(profileModules.concat(modules));
}
}, [profileDetails]);
const retrieveProfileDetails = useCallback(() => {
const profileListArr = profileList.split(",");
profileListArr.forEach((profileListArrMap) => {
ProfileMaintenanceService.retrieveProfileDetails(profileListArrMap).then(
(response) => {
setProfileDetails(response.data);
}
);
});
});
Full code:
import React, {
createContext,
useReducer,
useContext,
useState,
useEffect,
useLayoutEffect,
useCallback,
} from "react";
import { USER_PROFILE_ID_SESSION_ATTRIBUTE } from "../services/AuthenticationService";
import ProfileMaintenanceService from "../services/ProfileMaintenanceService";
import LoadingOverlay from "react-loading-overlay";
import FadeLoader from "react-spinners/FadeLoader";
var LayoutStateContext = createContext();
var LayoutDispatchContext = createContext();
export {
LayoutProvider,
useLayoutState,
useLayoutDispatch,
toggleSidebar,
toggleOffSidebar,
};
function toggleSidebar(dispatch) {
dispatch({
type: "TOGGLE_SIDEBAR",
});
}
function toggleOffSidebar(dispatch) {
dispatch({
type: "TOGGLE_OFF_SIDEBAR",
});
}
function layoutReducer(state, action) {
switch (action.type) {
case "TOGGLE_SIDEBAR":
return { ...state, isSidebarOpened: !state.isSidebarOpened };
case "TOGGLE_OFF_SIDEBAR":
return { ...state, isSidebarOpened: false };
case "CUSTOMIZE_SIDEBAR":
return { ...state, isSidebarOpened: false };
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
}
function LayoutProvider({ children }) {
const profileList = sessionStorage.getItem(USER_PROFILE_ID_SESSION_ATTRIBUTE);
const [profileDetails, setProfileDetails] = useState([]);
const [profileModules, setProfileModules] = useState([]);
const [loading, setLoading] = useState(true);
useLayoutEffect(() => {
retrieveProfileDetails();
}, []);
useLayoutEffect(() => {
if (profileDetails.length > 0) {
const modules = profileDetails.map((module) => module.module);
setProfileModules(profileModules.concat(modules));
}
}, [profileDetails]);
const retrieveProfileDetails = useCallback(() => {
const profileListArr = profileList.split(",");
profileListArr.forEach((profileListArrMap) => {
ProfileMaintenanceService.retrieveProfileDetails(profileListArrMap).then(
(response) => {
setProfileDetails(response.data);
}
);
});
});
var [state, dispatch] = useReducer(layoutReducer, {
isSidebarOpened: true,
modules: profileModules,
});
if (profileModules && profileModules.length > 0) {
return (
<LayoutStateContext.Provider value={state}>
<LayoutDispatchContext.Provider value={dispatch}>
{children}
</LayoutDispatchContext.Provider>
</LayoutStateContext.Provider>
);
} else {
return <LoadingOverlay active={true} spinner={<FadeLoader />} text="" />;
}
}
function useLayoutState() {
var context = useContext(LayoutStateContext);
if (context === undefined) {
throw new Error("useLayoutState must be used within a LayoutProvider");
}
return context;
}
function useLayoutDispatch() {
var context = useContext(LayoutDispatchContext);
if (context === undefined) {
throw new Error("useLayoutDispatch must be used within a LayoutProvider");
}
return context;
}
Upvotes: 0
Views: 464
Reputation: 202731
The initial modules state is []
from const [profileModules, setProfileModules] = useState([]);
higher up in the component. The useLayoutEffect
that setProfileModules(profileModules.concat(modules));
updates the value of profileModules
for the next render cycle. In the first render cycle when the component mounts the value of profileModules
is only ever able to be what the initial state is from the useState
hook.
Add a new action creator and case to handle updating the modules
state value when profileModules
will update
function updateModules(dispatch, modules) {
dispatch({
type: "UPDATE_MODULES",
modules,
});
}
function layoutReducer(state, action) {
switch (action.type) {
case "TOGGLE_SIDEBAR":
return { ...state, isSidebarOpened: !state.isSidebarOpened };
case "TOGGLE_OFF_SIDEBAR":
return { ...state, isSidebarOpened: false };
case "CUSTOMIZE_SIDEBAR":
return { ...state, isSidebarOpened: false };
case "UPDATE_MODULES":
return { ...state, modules: action.modules }; // <-- reduce new modules into state
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
}
Update the useLayoutEffect
that triggers on profileDetails
updating to dispatch updateModules
useLayoutEffect(() => {
if (profileDetails.length > 0) {
const modules = profileDetails.map((module) => module.module);
setProfileModules(profileModules.concat(modules));
updateModules(dispatch, modules); // <-- update modules
}
}, [dispatch, profileDetails]);
Upvotes: 1