Issue
I have a Context that is being provided to my whole app. In the context is a state of arrays that hold keys to filter the data being shown on the app. I am using this dropdown selector which is a tree selector that takes JSON data and displays it. It has a example of how to prevent re-render when parent renders but I can’t make it work for a functional component.
What is happening, when a selection is made in the dropdown it passes all currently selected to the onchange handler and in the handler it will set the state array in the context to the array passed by the dropdown. The state changes causes the dropdown component to re-render with the initial data passed by App as a prop resetting to having nothing checked.
I have looked at using React.memo to try and prevent the dropdown from re-rendering but can’t get it to work. Preventing the re-render of the dropdown is plan A in solving this problem.
Global Context Code
import React, {useState} from 'react';
//Typing for global state
type globalStateObj = {
Fleets: string[];
updateFleets: (value: string[])=>void;
}
//Context creation with initial
export const stateContext = React.createContext<globalStateObj>({
Fleets: [""],
updateFleets: ()=>{}
})
export const GlobalStateProvider = (props: {children: any}) =>{
const [fleets, setFleets] = useState([""]);//states should match stateContext
//Handlers for updating state
const updateFleetHandler = (value: string[])=>{
setFleets(value);
}
//Setting values to state and handlers
const conextValue: globalStateObj = {
Fleets: fleets,
updateFleets: updateFleetHandler,
}
return(
<stateContext.Provider value={conextValue}>
{props.children}
</stateContext.Provider>
)
};
Dropdown Component code
import { stateContext } from "../GlobalState";
import './DropdownFilterBar.scss';
import "react-dropdown-tree-select/dist/styles.css";
interface DropdownFilterBar_Props{
data: any[]
}
export const DropdownFilterBar = (props: DropdownFilterBar_Props) =>{
const globalState = useContext(stateContext);
const handleChange = (selected: any, allchecked: TreeNode[]) =>{
let results = allchecked.map(({value})=>value);
console.log(results);
globalState.updateFleets(results);
}
const texts: TextProps = {
placeholder:"Fleets",
inlineSearchPlaceholder:"Search"
}
return(
<div className="DropDownFilterBar">
<DropdownTreeSelect
data={props.data}
onChange={(selected, allchecked) =>{handleChange(selected, allchecked)}}
className="fleet-selector"
inlineSearchInput={true}
texts={texts}
/>
</div>
)
};
App.tsx Where ContextProvider is situated
<div className="App">
<Container className="themed-container">
<Row className='navRow'>
<Col><TopNavBar/></Col>
</Row>
<GlobalStateProvider>
<Row className="DropdownFilterRow">
<Col><DropdownFilterBar data={DropdownFilterData}/></Col>
</Row>
<Row>
<Col className="dashboard">
<Routes>
<Route path="/overview" element={<Home/>} />
...
Solution
The solution I ended up using was the useMemo hook instead of the React.Memo. useContext forces a re-render even with React.Memo. I realized useMemo can be used to memorize the JSX element with a dependency on the props.data. The dropdown component code looks like this now
const dropdown = useMemo(()=>{
return(
<DropdownTreeSelect
data={props.data}
onChange={(selected, allchecked) =>{handleChange(selected, allchecked)}}
className="fleet-selector"
inlineSearchInput={true}
texts={texts}
/>
)}, [props.data]);
return(
<div className="DropDownFilterBar">
{dropdown}
</div>
)
Answered By – dybka
Answer Checked By – Timothy Miller (BugsFixing Admin)