Reputation: 885
I am working on a React web application, I have a global state for storing some data, for some reason I happen to see quite unnecessarily renderings on my app!
I have component that looks like this:
const LanguageSkills = () => {
const [languageSelected, setLanguage] = React.useState({})
const { dispatch } = useContext(GlobalStore)
const handleChange = (name: string) => (event: any) => {
setLanguage({ ...languageSelected, [name]: event.target.checked })
}
useEffect(() => {
dispatch({
type: ApplicantActions.FILTER_APPLICANTS,
payload: {
languageSkills: languageSelected,
},
})
}, [dispatch, languageSelected])
return (
<>
{
{languageSkills.map((lang, index) => (
<TermWrapper key={index}>
<GreenCheckbox onChange={handleChange(lang.term)} value="checked" />
<Term>{lang.term}</Term>
</TermWrapper>
))}
}
</>
)
}
This component causes the parent component to render without any action doing from this component, well because the dispatch function is called immediately inside useEffect() once the component gets mounted (at least what I know), how else can I handle this so dispatch only gets called when I click the radio button? Is there any callback or someway that I can call dispatch once the local state changed using setState()?
Any idea how can I use dispatch in such situations?
Upvotes: 1
Views: 40
Reputation: 885
After several trying I came up with this and it seems fine for me, does not cause anymore parent component to re-render!
const LanguageSkills = () => {
const [isVisible, setIsVisible] = useState(false)
const [state, setState] = React.useState({})
const { dispatch } = useContext(GlobalStore)
const handleChange = (name: string) => (event: React.ChangeEvent<HTMLInputElement>) => {
const obj = { ...state, [name]: event.target.checked }
dispatchLanguageFilter(obj)
setState({ ...state, [name]: event.target.checked })
}
const dispatchLanguageFilter = useCallback(
obj => {
const languageSkills: string[] = []
for (const [key, value] of Object.entries(obj)) {
if (value) {
languageSkills.push(key)
}
}
dispatch({
type: ApplicantActions.FILTER_APPLICANTS,
payload: {
languageSkills: languageSkills,
},
})
},
[dispatch],
)
return (
<>
<Wrapper onClick={() => setIsVisible(!isVisible)}>
<FilterContainerTitle>Kielitaito</FilterContainerTitle>
<Arrow>{!isVisible ? <ArrowDown /> : <ArrowUp />}</Arrow>
</Wrapper>
{isVisible && (
<>
{languageSkills.map((lang, index) => (
<TermWrapper key={index}>
<GreenCheckbox onChange={handleChange(lang.term)} value="checked" />
<Term>{lang.term}</Term>
<TotalAmountWrapper>
<TotalAmount>{lang.totalAmount}</TotalAmount>
</TotalAmountWrapper>
</TermWrapper>
))}
</>
)}
</>
)
}
Not sure if its right way but mission accomplished!
Upvotes: 0
Reputation: 1124
Update this line
<GreenCheckbox onChange={handleChange(lang.term)} value="checked" />
to this
<GreenCheckbox onChange={() => handleChange(lang.term)} value="checked" />
you are calling the function in render time. give it a callback
UPDATED:
const handleChange = (name: string) => (event: any) => {
setLanguage({ ...languageSelected, [name]: event.target.checked })
}
useEffect(() => {
callMe();
}, [languageSelected])
const callMe = () => {
dispatch({
type: ApplicantActions.FILTER_APPLICANTS,
payload: {
languageSkills: languageSelected,
},
})
}
Upvotes: 1