• Stars
    star
    409
  • Rank 105,709 (Top 3 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 6 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Scroll a ReactNative View into the visible screen. Similar to DOMElement.scrollIntoView() browser function.

react-native-scroll-into-view

NPM NPM Build Status Twitter Follow

Scroll a ReactNative View ref into the visible portion of a ScrollView.

Similar to DOMElement.scrollIntoView() for web, with some extras.

yarn add react-native-scroll-into-view
// or
npm install react-native-scroll-into-view --save

There is no native code: this library is compatible with Expo managed workflow.

expo


Sponsor

ThisWeekInReact.com: the best newsletter to stay up-to-date with the React ecosystem:

ThisWeekInReact.com banner


Why ?

On long scrollable forms, can ensure errors become visible to the user on submit:

Formik example

Building some kind of "sections index":

Sections example

But really you are free to build whatever you want with it

Features:

  • Declarative component API
  • Imperative hook API
  • Configurable at many levels
  • Different alignment modes
  • Insets
  • Typescript definitions
  • Support for composition/refs/other ScrollView wrappers (Animated.ScrollView, react-native-keyboard-aware-scroll-view, glamorous-native...)

Note we don't plan to support anything else than ScrollView. Virtualized lists generally offer methods to scroll to a given index.

Minimal hooks example

import { View, Text, ScrollView } from 'react-native';
import {
  wrapScrollView,
  useScrollIntoView,
} from 'react-native-scroll-into-view';

const CustomScrollView = wrapScrollView(ScrollView);

function MyScreen() {
  return (
    <CustomScrollView>
      <MyScreenContent />
    </CustomScrollView>
  );
}

function MyScreenContent() {
  const scrollIntoView = useScrollIntoView();
  const viewRef = useRef();
  return (
    <>
      <Button onPress={() => scrollIntoView(viewRef.current)}>
        Scroll a view ref into view
      </Button>
      // in android if the scroll is not working then add renderToHardwareTextureAndroid this to view
      <View style={{ height: 100000 }}>
        <Text>Some long ScrollView content</Text>
      </View>

      <View ref={viewRef}>
        <Text>Will be scrolled into view on button press</Text>
      </View>
    </>
  );
}

API

import {
  ScrollIntoView, // enhanced View container
  wrapScrollView, // simple wrapper, no config
  wrapScrollViewConfigured, // complex wrapper, takes a config
  useScrollIntoView, // access hook for imperative usage
} from 'react-native-scroll-into-view';

// Available options with their default value
const options = {
  // auto: ensure element appears fully inside the view (if not already inside). It may align to top or bottom.
  // top: align element to top
  // bottom: align element to bottom
  // center: align element at the center of the view
  align: 'auto',

  // Animate the scrollIntoView() operation
  animated: true,

  // By default, scrollIntoView() calls are throttled a bit because it does not make much sense
  // to scrollIntoView() 2 elements at the same time (and sometimes even impossible)
  immediate: false,

  // Permit to add top/bottom insets so that element scrolled into view
  // is not touching directly the borders of the scrollview (like a padding)
  insets: {
    top: 0,
    bottom: 0,
  },

  // Advanced: use these options as escape hatches if the lib default functions do not satisfy your needs
  computeScrollY: (scrollViewLayout, viewLayout, scrollY, insets, align) => {},
  measureElement: viewRef => {},
};

// Wrap the original ScrollView
const CustomScrollView = wrapScrollView(ScrollView);

// Use the wrapped CustomScrollView as a replacement of ScrollView
function MyScreen() {
  return (
    <CustomScrollView
      // Can provide default options (overrideable)
      scrollIntoViewOptions={scrollIntoViewOptions}
    >
      <ScreenContent />
    </CustomScrollView>
  );
}

// Implement ScreenContent (inner of the ScrollView) with the useScrollIntoView and refs
function ScreenContent() {
  const scrollIntoView = useScrollIntoView();
  const viewRef = useRef();

  return (
    <>
      <Button
        onPress={() => {
          scrollIntoView(viewRef.current, options);
        }}
      >
        Scroll a view ref into view
      </Button>

      <View style={{ height: 100000 }}>
        <Text>Some long ScrollView content</Text>
      </View>

      <View ref={viewRef}>
        <Text>Will be scrolled into view on button press</Text>
      </View>
    </>
  );
}

// Or implement ScreenContent (inner of the ScrollView) with class + declarative ScrollIntoView component
class ScreenContent extends React.Component {
  render() {
    return (
      <>
        <ScrollIntoView>
          <Text>This will scroll into view on mount</Text>
        </ScrollIntoView>

        <ScrollIntoView align="center">
          <Text>This will scroll into view on mount and will be centered</Text>
        </ScrollIntoView>

        <ScrollIntoView animated={false}>
          <Text>This will scroll into view on mount without any animation</Text>
        </ScrollIntoView>

        <ScrollIntoView immediate={true}>
          <Text>
            This will not throttle scrollIntoView calls, as by default it does
            not make much sense to scroll into view multiple elements at the
            same time...
          </Text>
        </ScrollIntoView>

        <ScrollIntoView enabled={false}>
          <Text>This will scroll into view whenever enabled becomes true</Text>
        </ScrollIntoView>

        <ScrollIntoView scrollIntoViewKey="some string">
          <Text>
            This will scroll into view whenever scrollIntoViewKey changes
          </Text>
        </ScrollIntoView>

        <ScrollIntoView
          onMount={false}
          onUpdate={true}
          scrollIntoViewKey="some string"
        >
          <Text>
            This will scroll into on update (if it becomes enabled, or key
            changes)
          </Text>
        </ScrollIntoView>

        <ScrollIntoView scrollIntoViewOptions={options}>
          <Text>
            This will scroll into view on mount with custom option props
          </Text>
        </ScrollIntoView>

        <View>
          <ScrollIntoView
            enabled={false}
            ref={ref => (this.scrollIntoViewRef = ref)}
          >
            <Text>This will scroll into view when the button is pressed</Text>
          </ScrollIntoView>
          <Button
            title="Make above text scroll into view with custom options"
            onPress={() =>
              this.scrollIntoViewRef.scrollIntoView(scrollIntoViewOptions)
            }
          />
        </View>
      </>
    );
  }
}

You can also configure the HOC:

const CustomScrollView = wrapScrollViewConfigured({
  // SIMPLE CONFIG:
  // ScrollIntoView default/fallback options
  options: scrollIntoViewOptions,

  // ADVANCED CONFIG:
  // Use this if you use a ScrollView wrapper that does not use React.forwardRef()
  refPropName: 'ref',
  // unwraps the ref that the wrapped ScrollView gives you (this lib need the bare metal ScrollView ref)
  getScrollViewNode: ref => ref,
  // fallback value for throttling, can be overriden by user with props
  scrollEventThrottle: 16,
})(ScrollView);

All these hoc configurations can also be provided to the CustomScrollView as props.

Demos:

You can run the example folder as an Expo app with yarn start

It is also published on Expo

Basic example

Basic insets example

Scroll to next example

Sections example

Formik example

Recipes

Using in forms:

The integration with form libraries like Formik and Redux-form is very simple (see Formik example)

Formik integration

  • By default, the first error field of the form will reveal itself
  • enabled={!!error} means we'll only scroll into view fields that have an error
  • scrollIntoViewKey={submitCount} means we'll scroll into view fields which still have errors on every Formik submit attempt (submitCount is provided by Formik)

Using with react-native-keyboard-aware-scroll-view

KeyboardAwareScrollView does not forward refs by default so we need to obtain ref by using the innerRef prop:

const ScrollIntoViewScrollView = wrapScrollViewConfigured({
  refPropName: 'innerRef',
})(KeyboardAwareScrollView);

TODOs:

  • Tests
  • Universal/Web support
  • Support horizontal ScrollView?

Contribute

If your changes are impactful, please open an issue first.

License

MIT

Some code is inspired from contribution of @sebasgarcep of an initial scrollIntoView support for react-native-keyboard-aware-scroll-view

Hire a freelance expert

Looking for a React/ReactNative freelance expert with more than 5 years production experience? Contact me from my website or with Twitter.

More Repositories

1

react-async-hook

React hook to handle any async operation in React components, and prevent race conditions
TypeScript
1,140
star
2

awesome-debounce-promise

Debounce your API calls easily and stay in promised land.
TypeScript
376
star
3

scalable-frontend-with-elm-or-redux

An attempt to make Redux and Elm applications scale
JavaScript
363
star
4

trailing-slash-guide

Understand and fix your static website trailing slash issues!
HTML
307
star
5

gatsby-plugin-react-native-web

react-native-web plugin for Gatsby
JavaScript
277
star
6

react-native-animation-hooks

A simple declarative API for React-Native animations, using hooks
TypeScript
243
star
7

combine-promises

Like Promise.all(array) but with an object instead of an array.
TypeScript
202
star
8

rereduce

Reducer library for Redux
JavaScript
166
star
9

ajax-interceptor

This permits to wire interceptors on XHR requests and responses
JavaScript
122
star
10

react-reboot

The easiest way to refresh your React components with up-to-date syntax.
JavaScript
62
star
11

react-nested-loader

The easiest way to manage loaders/errors inside a button. NOT an UI lib.
JavaScript
62
star
12

sebastienlorber.com

My personal website
JavaScript
58
star
13

gcm-server-repository

maven repository for gcm-server for Android push notifications
Shell
46
star
14

react-native-alert-async

An Alert.alert that you can await
JavaScript
45
star
15

awesome-imperative-promise

Offer an imperative API on top of promise, with cancellation support
TypeScript
41
star
16

micro-typed-events

The smallest, most convenient typesafe TS event emitter you'll ever need
TypeScript
40
star
17

todomvc-onboarding

Showcase different ways to add a complex onboarding to a TodoMVC app
36
star
18

use-async-setState

Permits to await setState()
TypeScript
35
star
19

expo-typescript

TypeScript
33
star
20

redux-dispatch-subscribe

store enhancer for redux which allows to listen for dispatched actions
JavaScript
29
star
21

react-three-fiber-journey

Three.js Journey implemented with React-Three-Fiber
TypeScript
27
star
22

awesome-only-resolves-last-promise

Wraps an async function, and ensures that only last call will ever resolve/reject
TypeScript
26
star
23

docusaurus-crowdin-example

JavaScript
11
star
24

backspace-disabler

Disable the annoying browser backward navigation when the user press the backspace key
JavaScript
11
star
25

react-native-storage-slot

A simple typed wrapper around AsyncStorage
TypeScript
10
star
26

slorber

8
star
27

awesome-icu

Reference everything you find interesting about ICU translations
7
star
28

this-week-in-react

ThisWeekInReact.com
MDX
6
star
29

slo.im

slo.im shortcuts
4
star
30

react-updatable-context

React context which inject a value setter/updater in the Consumer
JavaScript
3
star
31

slorber-website

My website
JavaScript
2
star
32

test-docu

JavaScript
2
star
33

expo-ci

JavaScript
2
star
34

traced-svg-parameter-chooser

JavaScript
2
star
35

docusaurus-visual-tests

TypeScript
2
star
36

docusaurus-starter

JavaScript
1
star
37

bug-expokit-crashlytics

bug-expokit-crashlytics
JavaScript
1
star
38

slorber-api-screenshot

JavaScript
1
star
39

docusaurus-dsfr

TypeScript
1
star