react-swipeable

repository·main·Indexed 24 days ago

https://github.com/formidablelabs/react-swipeable

A lightweight React hook for detecting touch and mouse swipe gestures. It provides directional handlers (onSwipedLeft, onSwipedRight, onSwipedUp, onSwipedDown), lifecycle handlers (onSwipeStart, onSwiping, onTap), and configurable options for swipe distance (delta), duration, and browser scroll prevention. Compatible with react >= 16.8.3.

Tokens
7K
Snippets
18
Records
31
Agent score
85%

What's inside react-swipeable

  1. Get started with react-swipeable

    main

    To implement swipe functionality in your React application, follow these three steps:

    1. Import the useSwipeable hook from react-swipeable.
    2. Configure the swipe handlers by passing an options object to useSwipeable (e.g., defining onSwiped).
    3. Spread the returned handlers object onto the HTML element you want to track swipes on.

    This binds the necessary touch and mouse events to that specific element.

    import { useSwipeable } from 'react-swipeable';
    
    const handlers = useSwipeable({
        onSwiped: (eventData) => console.log("User Swiped!", eventData),
      ...config,
    });
    
    return <div {...handlers}> You can swipe here </div>;
  2. Prevent browser scrolling during swipes

    main

    Use the preventScrollOnSwipe prop to stop the browser from scrolling while a user is swiping.

    When preventScrollOnSwipe is true, Swipeable calls e.preventDefault() on the touchmove event. This only occurs if:

    1. preventScrollOnSwipe is true.
    2. trackTouch is true.
    3. The current swipe has an associated onSwiping or onSwiped handler/prop.

    Important Notes:

    • preventScrollOnSwipe supersedes touchEventOptions.passive for the touchmove listener. When true, the touchmove listener is set to { passive: false } to allow preventDefault(). Other listeners remain { passive: true }.
    • If you need all listeners to be passive for performance, consider using the CSS touch-action property on the container instead of using this prop.
  3. Set up the React-Swipeable documentation site locally

    main

    To run the documentation site for development, install the dependencies using yarn and then start the local development server with yarn start. The development server supports live reloading, so most changes will be reflected in the browser immediately without a restart.

    $ yarn
    $ yarn start
  4. Add a swipe listener to the `document`

    main

    To attach swipe functionality to the entire document instead of a specific DOM element, you can manually pass the document object to the ref returned by useSwipeable.

    Important: You must clean up the event listeners by calling ref({}) in the useEffect cleanup function to prevent memory leaks or unexpected behavior.

    const { ref } = useSwipeable({
      ...
    }) as { ref: RefCallback<Document> };
    
    useEffect(() => {
      ref(document);
      // Clean up swipeable event listeners
      return () => ref({});
    });
  5. Share a `ref` from `useSwipeable`

    main

    If you need to use both the ref provided by useSwipeable (to attach swipe handlers) and your own ref (to access the DOM element), you can use a ref passthrough function. Instead of passing a useRef object directly to the ref prop, pass a function that calls handlers.ref(el) and then assigns the element to your own ref.

    const MyComponent = () => {
      const handlers = useSwipeable({ onSwiped: () => console.log('swiped') })
    
      // setup ref for your usage
      const myRef = React.useRef();
    
      const refPassthrough = (el) => {
        // call useSwipeable ref prop with el
        handlers.ref(el);
    
        // set myRef el so you can access it yourself
        myRef.current = el;
      }
    
      return (<div {...handlers} ref={refPassthrough} />)
    }
  6. Prevent scrolling during swipes using `touch-action`

    main

    To prevent the page (or body) from scrolling while a user is swiping an element, you can use the CSS touch-action property. This is often a simpler and more performant alternative to using the preventScrollOnSwipe option in react-swipeable (which relies on event.preventDefault() during onTouchMove).

    Refer to the MDN documentation for touch-action to choose the appropriate CSS value for your specific interaction model.

  7. Migrate from Swipeable v6 to v7

    main

    When upgrading from version 6 to version 7, the primary breaking change involves the renaming of the property used to control scroll prevention during swipe gestures.

    Replace preventDefaultTouchmoveEvent with preventScrollOnSwipe. This prop provides the same functionality but with a more explicit name. In v7, this prop specifically controls the passive event listener option for touchmove events to ensure correct behavior.

    const handlers = useSwipeable({
    -  preventDefaultTouchmoveEvent: true,
    +  preventScrollOnSwipe: true,
    });
  8. Configure Swipeable Options

    main

    You can customize the behavior of the swipe detection using configuration props.

    • delta: The minimum distance (px) required before a swipe starts. This can be a number or an object to specify different thresholds for each direction (left, right, up, down). Unspecified directions default to 10.
    • swipeDuration: The maximum allowable duration (ms) for a swipe. If a swipe lasts longer than this value, it will not be considered a swipe, no callbacks will trigger, and tracking will stop. Defaults to Infinity.
    • trackTouch: Whether to track touch input (defaults to true).
    • trackMouse: Whether to track mouse input (defaults to false).
    • rotationAngle: Sets a rotation angle for swipe detection.
    • preventScrollOnSwipe: Prevents browser scrolling during a swipe (defaults to false).
    • touchEventOptions: Options for touch listeners (e.g., { passive: true }). Note that preventScrollOnSwipe supersedes this for the touchmove event.
    {
      delta: 10,                             // min distance(px) before a swipe starts. *See Notes*
      preventScrollOnSwipe: false,           // prevents scroll during swipe (*See Details*)
      trackTouch: true,                      // track touch input
      trackMouse: false,                     // track mouse input
      rotationAngle: 0,                      // set a rotation angle
      swipeDuration: Infinity,               // allowable duration of a swipe (ms). *See Notes*
      touchEventOptions: { passive: true },  // options for touch listeners (*See Details*)
    }
  9. Implement a simple carousel using useSwipeable

    main

    You can build a carousel by combining useSwipeable with a state management pattern (like useReducer) to handle directional movement.

    Key implementation details:

    • Use onSwipedLeft and onSwipedRight within the useSwipeable configuration to trigger movement functions.
    • Set swipeDuration (e.g., 500) to define the maximum duration of a swipe to be recognized.
    • Use preventScrollOnSwipe: true to ensure the swipe gesture doesn't trigger page scrolling.
    • Use trackMouse: true if you want the swipe gestures to work with a mouse as well as touch.
    • Spread the handlers returned by useSwipeable onto the wrapper element of your component.
    const Carousel: FunctionComponent<{children: ReactNode}> = (props) => {
      const numItems = React.Children.count(props.children);
      const [state, dispatch] = React.useReducer(reducer, getInitialState(numItems));
    
      const slide = (dir: Direction) => {
        dispatch({ type: dir, numItems });
        setTimeout(() => {
          dispatch({ type: 'stopSliding' });
        }, 50);
      };
    
      const handlers = useSwipeable({
        onSwipedLeft: () => slide(NEXT),
        onSwipedRight: () => slide(PREV),
        swipeDuration: 500,
        preventScrollOnSwipe: true,
        trackMouse: true
      });
    
      return (
        <div {...handlers}>
          <Wrapper>
            <CarouselContainer dir={state.dir} sliding={state.sliding}>
              {React.Children.map(props.children, (child, index) => (
                <CarouselSlot
                  order={getOrder(index, state.pos, numItems)}
                >
                  {child}
                </CarouselSlot>
              ))}
            </CarouselContainer>
          </Wrapper>
          <SlideButtonContainer>
            <SlideButton onClick={() => slide(PREV)} float="left">
              Prev
            </SlideButton>
            <SlideButton onClick={() => slide(NEXT)} float="right">
              Next
            </SlideButton>
          </SlideButtonContainer>
        </div>
      );
    };