The introduction

In the past, I worked on a small project about the development of H5 on mobile terminal using React + TSX. However, I didn’t know why it was necessary to adapt to the desktop terminal suddenly. In order to get a relatively consistent experience on both the mobile terminal and the desktop terminal, I used Better-Scroll.

Among them, I stepped on a lot of pits using Better-Scroll, now I will record some experience and configuration using Better-Scroll.

Why can’t I scroll

First, you need to understand why scrolling works. It’s simple: the height of the parent container is smaller than the height of the child element.

Before this, we first look at the browser scroll principle: browser scroll bar we will encounter, when the height of the page content exceeds the height of the viewport, there will be vertical scroll bar; A horizontal scroll bar appears when the width of the page content exceeds the viewport width. That is, when our viewport can not display the content, the user will scroll through the screen to see the rest of the content.

Second, the element you need to scroll through should be the first child of the parent element.

Note that BetterScroll handles scrolling of the first child element of the Wrapper by default and all other elements are ignored.

Third, why do I satisfy the above two, why can’t I scroll? If your content uses asynchronous data, the better scroll height will be the same as the original height, which of course will not be able to scroll. The solution is to refresh the asynchronous data after getting it. Or use the @better-scroll/observe-dom plugin to automatically update the height.

Configuration and initialization

Here I have used several plug-ins provided by Better Scroll, ObserveDOM, MouseWheel, ScrollBar, PullDown and Pullup.

General structure

import BScroll from '@better-scroll/core'
import { BScrollConstructor } from '@better-scroll/core/dist/types/BScroll'
import ObserveDOM from '@better-scroll/observe-dom'
import MouseWheel from '@better-scroll/mouse-wheel'
import ScrollBar from '@better-scroll/scroll-bar'
import PullDown from '@better-scroll/pull-down'
import Pullup from '@better-scroll/pull-up'

export interface ScrollProps {
  wrapHeight: string; prop? : any; onPullup? :Function; onPulldown? :Function;
}

const Scroll: React.FC<ScrollProps> = ({ wrapHeight, prop, onPullup, onPulldown, children,}) = > {
  BScroll.use(ObserveDOM)
  BScroll.use(MouseWheel)
  BScroll.use(ScrollBar)
  BScroll.use(PullDown)
  BScroll.use(Pullup)
  
 
 // ...

  return (
    <div className="scroll-warpper" ref={wrapRef} style={{ height: wrapHeight.overflow: 'hidden' }}>
      <div className="scroll-content">
        {children}
      </div>
    </div>)}export default Scroll
Copy the code

Ok, we’re done, now we’re ready to instantiate better Scroll

BetterScroll provides a class that instantiates a native DOM object as the first argument. Of course, if a string is passed, BetterScroll internally tries to call querySelector to retrieve the DOM object.

  // The outer wrap instance
  const wrapRef = useRef<HTMLDivElement>(null)

  // Record whether the Better scroll is instantiated to prepare for subsequent mount drop-down refresh and drop-down load
  const initRef = useRef(false)
  
  // Store an instance of better-Scroll
  const [scrollObj, setscrollObj] = useState<BScrollConstructor>()
  
  // Better Scroll configuration parameters
  const initBScroll = () = > {
    setscrollObj(
      new BScroll(wrapRef.current as HTMLDivElement, {
        //probeType is 3, which can send scroll events at any time, including scrollTo or momentum scrolling animation
        probetype: 3.// Native click can be used
        click: true.// Detect DOM changes
        observeDOM: true.// Mouse wheel Settings
        mouseWheel: {
          speed: 20.invert: false.easeTime: 300
        },
        // Displays a scroll bar
        scrollY: true.scrollbar: true.// Overanimate, the scroll bar will have an overanimate when downloading more
        useTransition: true.// Drop refresh
        pullDownRefresh: {
          threshold: 70.stop: 0
        },
        // Pull up to load more
        pullUpLoad: {
          threshold: 90.stop: 10}}}))Copy the code

Then, during the component mount phase, we instantiate the Better-Scroll and add drop and pull listeners to it

  // The object is initialized
  useEffect(() = > {
    initBScroll()
    return () = > {
      // Remember to destroy the component when uninstalling itscrollObj? .destroy() } }, [])// Drop refresh
  const pulldown = async () => {
    onPulldown && (await onPulldown())
    setTimeout(() = > {
      // Remember to use finishPullDown, otherwise you can only pull down oncescrollObj? .finishPullDown()// Your content changes after the drop down, and if you don't use refresh, you need to swipe to refresh the height of the contentscrollObj? .refresh() },500)}// Pull up load
  const pullup = async () => {
    onPullup && (await onPullup())
    setTimeout(() = >{ scrollObj? .finishPullUp() scrollObj? .refresh() },500)}// Object event mount
  useEffect(() = > {
    if (initRef.current === true) {
      // Drop refresh
      // Each update needs to remove the previous pullingDown event, otherwise it will accumulatescrollObj? .off("pullingDown"); scrollObj? .once("pullingDown", pulldown);

      // Pull up load
      // Each update needs to remove the previous pullingUp event, otherwise it will accumulatescrollObj? .off("pullingUp"); scrollObj? .once("pullingUp", pullup);
    } else {
      initRef.current = true;
    }
    // Why do I listen on prop because I can't listen on external state changes
    // handlePullUp's [...state,...res.data] state will always be the original []
  }, [prop]);
Copy the code

practice

import React, { CSSProperties, useEffect, useState, useCallback } from "react";
import Scroll from "./scroll";
import axios, { Method } from "axios";

export interface TestProps {}

interface ResponseType {
  code: number;
  data: any;
}

const Test: React.FC<TestProps> = () = > {
  const style: CSSProperties = {
    width: "500px"};const request = (url: string, method: Method): Promise<ResponseType> => {
    return new Promise((resolve, reject) = > {
      const options = {
        url,
        method,
      };
      axios(options)
        .then((res) = > {
          const data = res.data as ResponseType;
          resolve(data);
        })
        .catch((err) = > reject(err));
    });
  };

  const getData = () = > request("/api/datasource"."GET");

  const getMore = () = > request("/api/abc"."GET");

  const [state, setstate] = useState<any[]>([]);

  // Start by pulling data
  useEffect(() = >{(async function () {
      const res = await getData();
      console.log(res);
      res.code === 0 && setstate(res.data);
    })();
  }, []);

  const handlePullUp = useCallback(async() = > {const res = await getMore();
    res.code === 0 && setstate(state.concat(res.data));
  }, [state]);

  async function handlePullDown() {
    const res = await getData();
    res.code === 0 && setstate(res.data);
  }

  return (
    <div style={style}>
      <Scroll
        wrapHeight="300px"
        prop={state}
        onPullup={handlePullUp}
        onPulldown={handlePullDown}
      >
        {state.map((item, idx) =>
          idx % 2 === 0 ? (
            <div key={idx} style={{ height: "200px", background: "red}} ">
              {item}
            </div>
          ) : (
            <div key={idx} style={{ height: "200px", background: "green}} ">
              {item}
            </div>))}</Scroll>
    </div>
  );
};

export default Test;
Copy the code

The effect

conclusion

Better scroll is a very good tool, unfortunately I am not very familiar with react, I wasted a lot of time on the useEffect dependency parameter adjustment to solve the problem of pulling up and down multiple times and the parent component’s state is always empty. If you have a better solution, please leave a comment in the comments section, we will make progress together.

Refer to the link

2.0 guide BetterScroll

2.0 plug-in BetterScroll

When Better-Scroll meets Vue

I want to make a React version of the demo