3

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?

Here I click on tags, and it shows h1 element keeps re-rendering

5
  • 2
    You should inspect the elements panel to see if the <h1> element is actually being changed. It's possible that the re-rendering you're seeing is because the siblings of the <h1> element are being modified, thus their shared parent gets a reflow. You can also test this by moving <h1> outside of the <div> so that it is no longer a sibling. Commented Jan 17, 2020 at 16:33
  • Does this answer your question? Trace why a React component is re-rendering Commented Jan 17, 2020 at 16:44
  • @EmileBergeron No, I'm not passing any props to the <h1> element so that doesn't apply to my case. Commented Jan 17, 2020 at 17:25
  • But the enclosing component re-renders, which includes all its children as well. Commented Jan 17, 2020 at 17:28
  • @Wex I take a look at the inspector, and yes you are right the <h1> isn't being modified. It looks like the <body> is the one that's being modified, there is an <iframe> being added for very quick milliseconds but I don't remember adding any <iframe> Commented Jan 17, 2020 at 17:40

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Yes, I'm changing my tag state in onTagSelectChange. But aren't other components that don't receive any props should not be re-rendered?
1

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.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.