TilikWebDeloper
TilikWebDeloper

Reputation: 219

React Lazy Load useEffect

I have a problem getting clientWidth from useEffect.

There is a lazyLoad which loads a page with users.

import {Suspense, lazy} from 'react';

const UsersContainer = lazy (() => import ('./ users / users-container'));

const Index = (props) => {
    return (
        <Suspense fallback = {<div id = {'loading'} />}>
            <UsersContainer />
        </Suspense>
    )
}

export default Index;

UsersContainer has a child component dataTooltip, which displays the full User name if it does not fit into the block.

import React, {useEffect, useRef} from 'react';

import '../common.scss';

const DataTooltip = ({title, ... props}) => {
    let ref = useRef ();

    useEffect (() => {
        if (ref.current.scrollWidth> ref.current.clientWidth) {
            ref.current.setAttribute ('data-tooltip', title)
        }
    });

    return (
        <div ref = {ref} className = {'tooltip'}>
            {
                React.cloneElement (props.children, {ref: ref})
            }
        </div>
    )
}

export default DataTooltip;

What's the problem?

After the UsersContainer is loaded and rendered in the DOM, it has 'display: none' and at the same moment useEffect in DataTooltip is triggered asynchronously.

As a result, DataTooltip says that clientWidth = 0, due to the fact that the parent has 'display: none'.

How to make useEffect work after lazyLoad removed 'display: none'.

PS: useLayoutEffect works the same, clientWidth = 0


Solved the problem this way:

<Suspense fallback={<div id={'loading'}/>}>
   <Main/>
   <Acquaintance/>
   <UsersContainer/>
   <RegisterContainer/>
</Suspense>

to

<Suspense fallback={<div id={'loading'}/>}>
    <Main/>
</Suspense> 
            
<Suspense fallback={<div id={'loading'}/>}>
    <Acquaintance/> 
</Suspense>
            
<Suspense fallback={<div id={'loading'}/>}>
    <UsersContainer/>
</Suspense>
            
<Suspense fallback={<div id={'loading'}/>}>
    <RegisterContainer/>
</Suspense>

Upvotes: 5

Views: 9640

Answers (1)

Aasmund Berge Endresen
Aasmund Berge Endresen

Reputation: 347

I don't know if this solves your issue - but one thing I notice immediately is that you're missing the dependency array from your useEffect hook.

import React, {useEffect, useRef} from 'react';
...

const DataTooltip = ({title, ... props}) => {
    let ref = useRef ();

    useEffect (() => {
        if (ref.current.scrollWidth> ref.current.clientWidth) {
            ref.current.setAttribute ('data-tooltip', title)
        }
    });

    return (...)
}

export default DataTooltip;

should be:

import React, {useEffect, useRef} from 'react';
...

const DataTooltip = ({title, ... props}) => {
    let ref = useRef ();

    useEffect (() => {
        if (ref.current.scrollWidth> ref.current.clientWidth) {
            ref.current.setAttribute ('data-tooltip', title)
        }
    }, [ref]);

    return (...)
}

export default DataTooltip;

Also keep in mind that this will cause the component to re-render whenever ref changes, per the documentation of the useEffect hook you should always declare any variables from the upper scope used in the useEffect hook as part of the dependency array, and if you dont dont use any such variables you should pass an empty array still to prevent running an infinite loop.

Upvotes: 0

Related Questions