fullmoon6661
fullmoon6661

Reputation: 101

How to avoid unnecessary re-render for "static components" in react functional component?

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

Upvotes: 3

Views: 1703

Answers (3)

Jenya Tra
Jenya Tra

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

Michalis Garganourakis
Michalis Garganourakis

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

Vaintti
Vaintti

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

Related Questions