Search code examples
javascriptreactjsusecallback

Usecallback fail to memorization


I have the following code where changes on the parent component cause child element re - render. Basically the Menu component should be appear by right click on top of the placeholder tag but when it appears the whole parent component flickers. I used Usecallback with no luck. I tried useMemo but it doesn't accept any arguments. Since my callback functions are firing as a result of events, passing target of the event is important. Therefore I should pass the argument. I appreciate any suggestion.

const [menu, setMenu] = useState({isActive: false, position: undefined});

    <div className='placeholder'
        onClick={clickHandler}
        onContextMenu={rightClickHandler}>
        {menu.isActive && <Menu menu={menu} />}
        {[props.entity].map(plc => {
            let Content = place[props.entity];
            if(Content) {
                return <Content key={Math.random()} />
            }
        })}
    </div>

    const rightClickHandler = useCallback((e) => {
        e.preventDefault();
        const parentPosition = e.target.getBoundingClientRect();
        const position = {
            left: e.clientX - parentPosition.left,
            top: e.clientY - parentPosition.top
        };
        setMenu(
            {
                isActive: (menu.isActive ? menu.isActive: !menu.isActive),
                position: {
                    left: position.left,
                    top: position.top
                }
            }
        );
    }, []);

    const clickHandler = useCallback((e) => {
        setMenu({isActive: false, module: '', position: undefined});
    }, []);

Solution

  • Remove Math.random() It will reinforce the component to re render

    const [menu, setMenu] = useState({isActive: false, position: undefined});
    
        <div className='placeholder'
            onClick={clickHandler}
            onContextMenu={rightClickHandler}>
            {menu.isActive && <Menu menu={menu} />}
            {[props.entity].map((plc, i) => {
                let Content = place[props.entity];
                if(Content) {
                    return <Content key={'somethingElse' + i} />
                }
            })}
        </div>
    
        const rightClickHandler = useCallback((e) => {
            e.preventDefault();
            const parentPosition = e.target.getBoundingClientRect();
            const position = {
                left: e.clientX - parentPosition.left,
                top: e.clientY - parentPosition.top
            };
            setMenu(
                {
                    isActive: (menu.isActive ? menu.isActive: !menu.isActive),
                    position: {
                        left: position.left,
                        top: position.top
                    }
                }
            );
        }, []);
    
    
    
     const clickHandler = useCallback((e) => {
                setMenu
    
    ({isActive: false, module: '', position: undefined});
        }, []);