I have a navbar component containing a dropdown which allows user to change their "region" and it updates the redux store. The update to redux store is working properly. However, it is not re-rendering the parent component.
From this navbar component I use the changeRegion ActionCreator to update the store. I can see the update in localstorage and can even console.log it and see the proper region after its changed. What is NOT happening, is the table in the "UserManagement" component is not updating.
I use setPageData
in useEffect()
in UserManagement.js to determine what state the table is in. Basically, if there is no region selected (initialState is blank) then it should show an empty table with a dropdown to select region. Once a region is selected, it should then display the data in table.
Clearly I am just missing something, but I have been trying to make this work for way too long and really could use some help.
import React, { useState, useEffect } from "react";
import {
Box,
ButtonDropdown,
Table,
} from "private-compontent";
import fakeData from "../fakeData"
import { useSelector, useDispatch } from 'react-redux'
import { bindActionCreators } from 'redux'
import { actionCreators } from '../../../state/actionsCreators'
export const DetailsTable = (props) => {
const [pageData, setPageData] = useState([]);
const region = useSelector((state) => state.region)
const dispatch = useDispatch();
const { changeRegion } = bindActionCreators(actionCreators, dispatch)
const [regionState, setRegionState] = useState(region);
const currentUserRegion = region === '' ? "Select Region" : "Region: " + region;
useEffect(() => {
if (region && region !== '') {
setRegionState(region)
setPageData(fakeData);
} else {
setPageData([{}]);
}
}, []);
// When a new region is selected, save it to localStorage and setRegion
const regionChange = (event) => {
changeRegion(event.detail.id)
}
function EmptyState({ title, subtitle, action }) {
return (
<Box textAlign="center" color="inherit">
<Box variant="strong" textAlign="center" color="inherit">
{title}
</Box>
<Box variant="p" padding={{ bottom: 's' }} color="inherit">
{subtitle}
</Box>
{action}
</Box>
);
}
return (
<>
<Table
{...collectionProps}
items={ items }
empty={
<EmptyState
title="No Data"
subtitle="No data to display"
action={
<ButtonDropdown
items={[
{ text: "NA", id: "NA", disabled: false },
{ text: "EU", id: "EU", disabled: false },
{ text: "FE", id: "FE", disabled: false },
]}
onItemClick={regionChange}
>
{currentUserRegion}
</ButtonDropdown> }
/>
}
/>
</>
);
};
export default DetailsTable;
import React from "react";
import { ButtonDropdown} from "../ButtonDropdown";
import { useSelector, useDispatch } from 'react-redux'
import { bindActionCreators } from 'redux'
import { actionCreators } from '../../../state/actionsCreators'
export const Navbar = () => {
const region = useSelector((state) => state.region)
const dispatch = useDispatch();
const { changeRegion } = bindActionCreators(actionCreators, dispatch)
// When a new region is selected, save it to localStorage and setRegion
const regionChange = (event) => {
changeRegion(event.detail.id)
}
const currentUserRegion = region === '' ? "Select Region" : "Region: " + region;
// Construct the ButtonDropdown based on the currentPath
const regionButtonDropdown =
// If UserManagement show region options
<ButtonDropdown
items={[
{ text: "NA", id: "NA", disabled: false },
{ text: "EU", id: "EU", disabled: false },
{ text: "FE", id: "FE", disabled: false },
]}
onItemClick={regionChange} // Run regionChange on dropdown change
>
{currentUserRegion}
</ButtonDropdown>
// Return our HTML
return (
<>
<header id="navbar">
{regionButtonDropdown}
</header>
</>
);
};
export default Navbar;
import { combineReducers } from "redux"
import regionReducer from "./regionReducer"
const reducers = combineReducers({
region: regionReducer
})
export default reducers;
import * as ACTIONS from '../actionsTypes'
const initialState = '';
const reducer = (state = initialState, action) => {
switch (action.type) {
case ACTIONS.CHANGE_REGION:
return action.payload;
default:
return state;
}
}
export default reducer;
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import logger from 'redux-logger';
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web
import rootReducer from './reducers/reducers'
const persistConfig = {
key: 'root',
storage
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const middleware = applyMiddleware(thunk, logger);
const store = createStore(
persistedReducer,
middleware
);
const persistor = persistStore(store);
export { store, persistor };
Your useMemo
only runs on page load. Change it to
useEffect(() => {
if (region && region !== '') {
setRegionState(region)
setPageData(fakeData);
} else {
setPageData([{}]);
}
}, [region]);
So that it runs whenever region
changes. See Conditionally firing an effect from the React docs.
Specifically note,
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run.