I know theres multiple questions along this topic on here, but I’ve incorporated what I’ve found and still getting no where. It should be straightforward, I have a component with a Select that the initial value is ”, then updated through state when data comes in (defaultPlan). The label for this selection gets rendered on the menu just fine. But when I change the selection, the renderValue does not update, instead I only see the same label from the defaultPlan, while the actual value is changing appropriately to the selected option. I’ve tried adding a key with state directly for the Select component in case it was an issue of not rerendering due to shallow comparison, but that did not change anything (also tried with a useEffect specifcally watching for the other state values to change). I tried calling my renderValue function under the handleChange function, but that did not work either- which makes sense since the event is a different object and does not have the full menu item option object to pass to it, but again, I’m out of ideas. Any suggestions are appreciated, just not seeing what I’m doing wrong.
Here’s the component:
const ClientGoalReport = () => {
const { clientId } = useParams();
const [ planId, setPlanId ] = useState()
const [ planList, setPlanList ] = useState([])
const [ defaultPlan, setDefaultPlan ] = useState('')
const [ isStart, setIsStart ] = useState()
const [ isEnd, setIsEnd ] = useState()
const [ header, setHeader ] = useState()
const [tableData, setTableData] = useState()
const [hasData, setHasData] = useState('initial')
const [renderKey, setRenderKey] = useState(0);
const { data: plans } = useGetAllClientReportsQuery(clientId)
useEffect(() => {
if (plans) {
let list = plans.map((plan, idx) => (
{
value: {start: plan.startDate, end: plan.endDate, planId: plan._id},
label: `${idx+1} ${plan.type} Report Date: ${new Date(plan.reportDate).toLocaleDateString()}, Start: ${new Date(plan.startDate).toLocaleDateString()}, End: ${new Date(plan.endDate).toLocaleDateString()} `
}
)).sort((a, b) => a.startDate - b.startDate)
// console.log("list: ", list)
setPlanList(list);
const def = list[list.length -1]
setDefaultPlan(def)
}
}, [plans])
const [pullData, { data, isSuccess }] = useGetGoalAttainDataMutation()
const generatePlanOptions = () => {
return planList.map((plan, idx) => {
return (
<MenuItem key={idx} value={plan.value}>
{plan.label}
</MenuItem>
)
})
}
const renderValue = (selected) => {
if (!selected) return '';
return selected.label;
}
const handlePlanChange = (event) => {
// renderValue();
setRenderKey((prevKey) => prevKey + 1);
setPlanId(event.target.value.planId)
setIsStart(event.target.value.start)
setIsEnd(event.target.value.end)
}
useEffect(() => {
//takes data and constructs table to display
}
}, [data, isSuccess, isStart, isEnd])
return (
<>
<div display='flex'>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
p: 1,
m: 1,
borderRadius: 1,
}}
>
<h1>Search by Service Plan</h1><br />
<FormControl sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="plan-select-label">Plan</InputLabel>
<Select
labelId="plan-select-label"
label="Plan"
name='plan'
value={defaultPlan}
onChange={handlePlanChange}
autoWidth
key={renderKey}
renderValue={renderValue}
native={false}
displayEmpty={true}
>
{generatePlanOptions()}
</Select>
</FormControl><br />
<Button color='primary' variant='contained' onClick={() => pullData({clientId, isStart, isEnd, planId})}> Run </Button>
</Box>
</div>
{displayReport}
</>
)
}
2
Answers
After Nazrul's answer and thinking it through, the issue I think comes mainly down to how I construct the menu options from the data and that my values are objects (which have the different fields needed to make the api call- its a mongodb aggregate and requires different values to set the table correctly). To make it work, I updated the handlePlanChange to run a match of the planList used to create the menu with the selected plan's id, then update the state which gets the renderValue to respond appropriately:
(Below theres a minor change where I changed defaultPlan to isPlan just to be more succinct since its used beyond just setting the default option. I also removed the renderKey state and key prop, since that was not actually needed)
It could be the way you are updating the state for the selected plan. When the selection changes, you are updating the planId, isStart, and isEnd states based on the event.target.value, which corresponds to the selected value of the component. However, you are not updating the defaultPlan state, which is used as the value prop for the component.
Try modifying your handlePlanChange function to update the defaultPlan state properly,
By updating the defaultPlan state with the selected plan, it should reflect the changes in the component.
Also, make sure to update the value prop of the component to be planId instead of defaultPlan,