Reputation: 101
I have a react functional component that shows list of tags and posts + few static text/decorations. I store the currently selected tag in a state using useState
hook. Posts are fetched by using apollo's useQuery
hook with tag
variable. User should able to select a tag and it will replace the current tag
state - thus the useQuery(POSTS_QUERY)
will re-run with new tag
variable.
const onTagSelectChange = (window: Window,
router: NextRouter,
name: string,
checked: boolean,
tagSetter: React.Dispatch<React.SetStateAction<string>>) => {
if (checked) {
setTagQueryInUrl(window, router, name)
tagSetter(name)
} else {
setTagQueryInUrl(window, router, null)
tagSetter(null)
}
}
const NewsList: NextPage = () => {
const router = useRouter()
const query = router.query as Query
// store tag in state
// initialize tag from `tag` query
const [tag, setTag] = useState(query.tag)
const { data: postsData, loading: postsLoading, error: postsError } = useQuery(
POSTS_QUERY,
{
variables: {
tag: tag
}
}
)
const { data: tagsData, loading: tagsLoading, error: tagsError } = useQuery(TAGS_QUERY)
// show error page if either posts or tags query returned error
if (postsError || tagsError) {
return <Error statusCode={500} />
}
return (
<div>
<h1>Here we have list of news, and I should not re-render everytim :(</h1>
<Tags
loading={tagsLoading}
data={tagsData}
isChecked={(name) => name === tag}
onChange={(name, checked) => onTagSelectChange(window, router, name, checked, setTag)}
/>
<Posts loading={postsLoading} data={postsData} />
</div>
)
}
My question is, why is my h1
block keeps re-rendering even though I don't pass anything to it? Or do I completely misunderstand how react works?
Upvotes: 3
Views: 1703
Reputation: 1
Gut answer to subject question:
useMemo(() => <h1>Neva rerenda!</h1>, [])
// Also works with props
useMemo(() => <SoHeavyRenderer foo={foo}>{bar}</SoHeavyRenderer>, [foo, bar])
// Another option to have the declaration outside of the rendering
const h1 = <h1>Neva rerenda!</h1>
const NewsList: NextPage = () => {
// ... your function here can use {h1}
}
My question is, why is my h1 block keeps re-rendering even though I don't pass anything to it?
I think answers can be "to save resource", or maybe "that's how they did it in react". So far I don't see why it would be a structural issue. Of my knowledge: JSX expression replaced with React.createElement(), which returns an EcmaScript Object (a "plain object"). To skip a render, the rendering procedure equality check would have to be a "deep equal", which might be a significant cost increase. On the other hand, skipping renders would be a cost save.
Hopefully someone could elaborate on that.
Upvotes: 0
Reputation: 2930
As your state is declared on your NewsList
component, any state change (as another user stated on his answer) will trigger a re-render of the whole component (NewList) and not only to the components that you have passed your state (thus to the static <h1>
you have in there).
If there are parts of this component that have nothing to do with this state, you can move them outside to avoid the re-render.
Though, on cases like this, re-rendering your <h1>
is not a cost for React. You should worry and follow this approach on custom components where more complex things going on (e.g. populating lists or calculating stuff etc..). In those cases, you don't want all this complex stuff to happen again, if they are not affected by a parent's state change. You should also always consider, if moving the component outside makes sense or by doing so, you make your code complex.
You should always strike a balance between well-organized and efficient code.
Upvotes: 1
Reputation: 166
React components re-render whenever their state or props change. If I am reading this correctly then you are changing tag in state whenever the url changes and thus making the component to re-render itself.
Upvotes: 3