无限滚定的最佳实践

295 阅读1分钟

#index.jsx

import './home.css'

const THRESHOLD = 15;
const SlidingWindowScrollHook = (props) => {
  const [start, setStart] = useState(0);
  const [end, setEnd] = useState(THRESHOLD);
  const [observer, setObserver] = useState(null);
  const $bottomElement = useRef();
  const $topElement = useRef();

  useEffect(() => {
    intiateScrollObserver();
    return () => {
      resetObservation()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [start, end])

  const intiateScrollObserver = () => {
    const options = {
      root: null,
      rootMargin: '0px',
      threshold: 0.1
    };
    const Observer = new IntersectionObserver(callback, options)
    if ($topElement.current) {
      Observer.observe($topElement.current);
    }
    if ($bottomElement.current) {
      Observer.observe($bottomElement.current);
    }
    setObserver(Observer)
  }

  const callback = (entries, observer) => {
    console.log(entries, '===>')
    entries.forEach((entry, index) => {
      const listLength = props.list.length;
      console.log(entry.target)
      // Scroll Down
      if (entry.isIntersecting && entry.target.id === "bottom") {
        const maxStartIndex = listLength - 1 - THRESHOLD;     // Maximum index value `start` can take
        const maxEndIndex = listLength - 1;                   // Maximum index value `end` can take
        const newEnd = (end + 10) <= maxEndIndex ? end + 10 : maxEndIndex;
        const newStart = (end - 5) <= maxStartIndex ? end - 5 : maxStartIndex;
        setStart(newStart)
        setEnd(newEnd)
      }
      // Scroll up
      if (entry.isIntersecting && entry.target.id === "top") {
        const newEnd = end === THRESHOLD ? THRESHOLD : (end - 10 > THRESHOLD ? end - 10 : THRESHOLD);
        let newStart = start === 0 ? 0 : (start - 10 > 0 ? start - 10 : 0);
        setStart(newStart)
        setEnd(newEnd)
      }

    });
  }
  const resetObservation = () => {
    observer && observer.unobserve($bottomElement.current);
    observer && observer.unobserve($topElement.current);
  }


  const getReference = (index, isLastIndex) => {
    if (index === 0)
      return $topElement;
    if (isLastIndex)
      return $bottomElement;
    return null;
  }

  const { list, height } = props;
  const updatedList = list.slice(start, end);
  const lastIndex = updatedList.length - 1;

  return (
    <ul style={{ position: 'relative' }}>
      {updatedList.map((item, index) => {
        const top = (height * (index + start)) + 'px';
        const refVal = getReference(index, index === lastIndex);
        const id = index === 0 ? 'top' : (index === lastIndex ? 'bottom' : '');
        return (<li className="li-card" key={item.key} style={{ top }} ref={refVal} id={id}>{item.value}</li>);
      })}
    </ul>
  );
}


let MY_ENDLESS_LIST = new Array(10000).fill({
  key: 'index',
  value: 'A' + 'index'
}).map((item, index) => ({
  key: index,
  value: 'A' + index
}))





export default function Home() {
  return <div>

    主要开发测试一些demo12312

    <SlidingWindowScrollHook list={MY_ENDLESS_LIST} height={195}></SlidingWindowScrollHook>

  </div>;


}

#home.css

.li-card {
    display: flex;
    justify-content: center;
    list-style: none;
    box-shadow: 2px 2px 9px 0px #bbb;
    padding: 70px 0;
    margin-bottom: 20px;
    border-radius: 10px;
    position: absolute;
    width: 80%;
  
}