Function Components are different to Class Components.
In Class Components the aim is to keep the number of rerenders to a minimum as the component does not have the fine grain control over memoization that is provided with Function Components and hooks.
That's not to say that Function Components should not care about rerenders, but you should not be afraid of rerenders. Using correct state and memoization management in most cases a rerender should be indiscernible to the user.
useList
export default function useList() {
// useQuery Assumptions:
// - internally makes proper use of memoization or
// external cache / storage to ensure heavier
// calculations are only run when needed.
// - data will remain the same reference each render
// unless data has changed.
let { data } = useQuery(GET_LIST);
// only recalculate return when data from useQuery
// has changed reference.
return useMemo(() => {
return {
list: _.cloneDeep(data)?.menuTree?.filter((i) => i.parentId === null) || [],
otherMenus: data?.menuTree || []
};
}, [data]);
}
Sidebar
import SearchInput from './searchInput'
const Sidebar = () => {
// list should now be only be receiving a new reference whenever
// data from useQuery receives a new reference.
const { list } = useList();
// state value and setter used to store search term.
const [searchValue, setSearchValue] = useState<string>('');
// useEffect will run on component mount and whenever
// the list value from useList receives a new reference
useEffect(() => {
console.log('list :>> ', list)
}, [list]);
// render out search input.
return (
<SearchInput searchValue={searchValue} setSearchValue={setSearchValue} />
)
}
// Most of the time React.memo is the incorrect tool to use,
// heavy calculations should be handled with memoization
// hooks such as useMemo, useCallback, etc.
export default Sidebar
SearchInput
const SearchInput = ({ searchValue, setSearchValue }) => {
// Render controlled search input.
return (
<div>
<Input
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
/>
</div>
)
}
// Most of the time React.memo is the incorrect tool to use,
// heavy calculations should be handled with memoization
// hooks such as useMemo, useCallback, etc.
export default SearchInput
Anytime that SearchInput inside of Sidebar calls its setSearchValue prop a rerender of the Sidebar component will be triggered due to an update of its state value searchValue.
This in turn will rerun the useList and useQuery hooks, but if my useQuery assumptions are correct this should be either insignificant or required:
- Insignificant:
data from useQuery has not changed since the last render and no heavy calculations will be run within useQuery, as no new reference was given to data the useMemo with dependency array [data] will not be recalculated, avoiding the heavy cloneDeep function.
- Required:
data from useQuery has received a new reference signifying a change in the value stored within and possible recalculation of heavy memoized values. This means that the useMemo with dependency array [data] will be recalculated running the heavy cloneDeep function. This would be necessary to ensure the user is seeing the most relevant data.
Alternative Option
If the list value will have no interaction with the searchValue value you could create a wrapping component to handle the search logic / ui of the Sidebar (lets call this SidebarSearch).
Sidebar
import SidebarSearch from './sidebarSearch'
const Sidebar = () => {
const { list } = useList();
useEffect(() => {
console.log('list :>> ', list)
}, [list]);
// render out search input.
return (
<SidebarSearch />
)
}
export default Sidebar
SidebarSearch
import SearchInput from './searchInput'
const SidebarSearch = () => {
// state value and setter used to store search term.
const [searchValue, setSearchValue] = useState<string>('');
// render out search input.
return (
<SearchInput searchValue={searchValue} setSearchValue={setSearchValue} />
)
}
export default SidebarSearch
This would mean that whenever SearchInput calls the setSearchValue prop it would trigger a rerender starting from SidebarSearch avoiding useList being rerun.