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.