I was using a free React template which was implemented with Context+Reducer and soon, after adding complexity, I ran accross the classic re-render caveat of useContext API. Is it possible to change my Context interface to one of Redux, since I don't want to re-render everything each time a single action is dispatched. I am currently using React v17.0.2
import { createContext, useContext, useReducer, useMemo } from "react";
// prop-types is a library for typechecking of props
import PropTypes from "prop-types";
//React main context
const MaterialUI = createContext();
// Setting custom name for the context which is visible on react dev tools
MaterialUI.displayName = "MaterialUIContext";
//React reducer
function reducer(state, action) {
switch (action.type) {
case "MINI_SIDENAV": {
return { ...state, miniSidenav: action.value };
}
case "RESET_IDS": {
return { ...state, chartIds: ["Main"] };
}
case "ADD_CHART": {
return { ...state, chart: action.value, chartIds: [...state.chartIds, action.id] };
}
case "DELETE_CHART": {
return {
...state,
chart: action.value,
chartIds: state.chartIds.filter((id) => id !== action.id),
};
}
case "FILE_DATA": {
return { ...state, fileData: action.value };
}
case "ANALYSIS_DATA": {
return { ...state, analysisData: action.value };
}
case "DATA_UPLOADED": {
return { ...state, isUploaded: action.value };
}
case "LAYOUT": {
return { ...state, layout: action.value };
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
}
// React context provider
function MaterialUIControllerProvider({ children }) {
const initialState = {
miniSidenav: false,
chart: {},
chartIds: ["Main"],
fileData: [],
analysisData: [],
isUploaded: false,
layout: "page",
darkMode: false,
};
const [controller, dispatch] = useReducer(reducer, initialState);
const value = useMemo(() => [controller, dispatch], [controller, dispatch]);
return <MaterialUI.Provider value={value}>{children}</MaterialUI.Provider>;
}
//React custom hook for using context
function useMaterialUIController() {
const context = useContext(MaterialUI);
if (!context) {
throw new Error(
"useMaterialUIController should be used inside the MaterialUIControllerProvider."
);
}
return context;
}
// Typechecking props for the MaterialUIControllerProvider
MaterialUIControllerProvider.propTypes = {
children: PropTypes.node.isRequired,
};
// Context module functions
const setMiniSidenav = (dispatch, value) => dispatch({ type: "MINI_SIDENAV", value });
const resetIds = (dispatch) => dispatch({ type: "RESET_IDS" });
const addNewChart = (dispatch, value, id) => dispatch({ type: "ADD_CHART", value, id });
const deleteChart = (dispatch, value, id) => dispatch({ type: "DELETE_CHART", value, id });
const setFileData = (dispatch, value) => dispatch({ type: "FILE_DATA", value });
const setAnalysisData = (dispatch, value) => dispatch({ type: "ANALYSIS_DATA", value });
const setIsUploaded = (dispatch, value) => dispatch({ type: "DATA_UPLOADED", value });
const setLayout = (dispatch, value) => dispatch({ type: "LAYOUT", value });
export {
MaterialUIControllerProvider,
useMaterialUIController,
setMiniSidenav,
resetIds,
addNewChart,
deleteChart,
setFileData,
setAnalysisData,
setIsUploaded,
setLayout,
};
I was trying to update fileData state and I ran across a breaking issue involving infinite re-renders, aswell as some non breaking issues regarding the rendering speed and processing! Any help is much appreciated
its pretty straightforward. After you have installed redux and redux toolkit. You can refactor your state setup sth like this:
import { createSlice, configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
// define initial state
const initialState = {
miniSidenav: false,
chart: {},
chartIds: ["Main"],
fileData: [],
analysisData: [],
isUploaded: false,
layout: "page",
darkMode: false,
};
// define the slice
const materialSlice = createSlice({
name: 'material',
initialState,
reducers: {
setMiniSidenav: (state, action) => {
state.miniSidenav = action.payload;
},
resetIds: (state) => {
state.chartIds = ["Main"];
},
addNewChart: (state, action) => {
state.chart = action.payload.value;
state.chartIds = [...state.chartIds, action.payload.id];
},
deleteChart: (state, action) => {
state.chart = action.payload.value;
state.chartIds = state.chartIds.filter((id) => id !== action.payload.id);
},
setFileData: (state, action) => {
state.fileData = action.payload;
},
setAnalysisData: (state, action) => {
state.analysisData = action.payload;
},
setIsUploaded: (state, action) => {
state.isUploaded = action.payload;
},
setLayout: (state, action) => {
state.layout = action.payload;
},
},
});
export const {
setMiniSidenav,
resetIds,
addNewChart,
deleteChart,
setFileData,
setAnalysisData,
setIsUploaded,
setLayout,
} = materialSlice.actions;
// configure store
const store = configureStore({
reducer: materialSlice.reducer,
});
// context provider
function MaterialUIControllerProvider({ children }) {
return <Provider store={store}>{children}</Provider>;
}
export { MaterialUIControllerProvider };
then you can use useDispatch and useSelector in your components to select a state and change them accordingly. As for infinite loop you might wanna check you are actually dispatching the action. Maybe it has something to do with dependencies inside your useEffect?