简体   繁体   中英

custom hook performance issues in react

I have re-written this javascript code logic to a react custom hook to use.

But it seems like the solution i have made is a bit slow, and heavy on performance, I was wondering in which sort of ways I could make it more performant.

This is the custom hook:

import { useEffect, useState } from 'react';
import { isBrowser } from '@utils/environment';
import { NAVBAR_HEIGHT_PX } from '@common/helpers';

export const useSticky = stickyElement => {
  const [offset, setOffset] = useState(NAVBAR_HEIGHT_PX);
  let offsetTop = NAVBAR_HEIGHT_PX;
  let currPos = isBrowser ? window.pageYOffset : 0;
  let endScroll = isBrowser ? window.innerHeight - stickyElement?.current?.offsetHeight - 500 : 0;

  useEffect(() => {
    if (!isBrowser) {
      return undefined;
    }

    const positionStickySidebar = () => {
      const { innerHeight, scrollY } = window;

      endScroll = innerHeight - stickyElement.current.offsetHeight;

      if (window.scrollY < currPos) {
        if (offsetTop < NAVBAR_HEIGHT_PX * 2) {
          offsetTop = offsetTop + currPos - scrollY;
        } else if (offsetTop >= NAVBAR_HEIGHT_PX * 2 && offsetTop !== endScroll) {
          offsetTop = NAVBAR_HEIGHT_PX * 2;
        }
      } else {
        if (offsetTop > endScroll) {
          offsetTop = offsetTop + currPos - scrollY;
        } else if (offsetTop < (endScroll) && offsetTop !== endScroll) {
          offsetTop = endScroll;
        }
      }
      setOffset(offsetTop);
      currPos = window.scrollY;
    };

    window.addEventListener('resize', positionStickySidebar, {
      capture: false,
      passive: true,
    });
    window.addEventListener('scroll', positionStickySidebar, {
      capture: false,
      passive: true,
    });

    return () => {
      window.removeEventListener('scroll', positionStickySidebar);
      window.removeEventListener('resize', positionStickySidebar);
    };
  }, []);

  return offset;
};

This is the usage of it

const divRef = useRef(null);
const offsetTop = useSticky(divRef);

  <ContentContainer offsetTop={offsetTop} ref={divRef}>

And this is the styled component:

export const ContentContainer = styled.div`
  position: sticky;
  top: ${({ offsetTop }) => `${offsetTop}px`};
`;

I would suggest two things:

  1. Move all variables (except useState) inside useCallback function. This way on each render these values will not be recalculated. Especially that important for endScroll variable, because it uses offsetHeight in calculations. Each invocation of offsetHeight causes reflow process in browser, which impacts performance a lot. So calling it on each render is not something very good, especially in your case where such calculations doesn't affect anything (except on first render).
  // Instead of this:
  export const useSticky = stickyElement => {
    // ...
    let offsetTop = NAVBAR_HEIGHT_PX;
    let currPos = isBrowser ? window.pageYOffset : 0;
    let endScroll = isBrowser ? window.innerHeight - stickyElement?.current?.offsetHeight - 500 : 0;
    // ...
  }


  // Do this:
  export const useSticky = stickyElement => {
    // ...
    useEffect(() => {
      if (!isBrowser) return undefined;
      let offsetTop = NAVBAR_HEIGHT_PX;
      let currPos = isBrowser ? window.pageYOffset : 0;
      let endScroll = isBrowser ? window.innerHeight - stickyElement?.current?.offsetHeight - 500 : 0;
      const positionStickySidebar = () => {
    // ...
  }
  1. Get rid of offset state ( const [offset, setOffset] =... ) if possible. Performance should become much better if you replace
const [offset, setOffset] = useState(NAVBAR_HEIGHT_PX);
// ...
setOffset(offsetTop);
// ...
return offsetTop;

with

const topOffsetRef = useRef(NAVBAR_HEIGHT_PX);
// ...
stickyElement.current.style.top = offsetTop + 'px';
topOffsetRef.current = offsetTop;
// ...
return topOffsetRef.current;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM