Code Monkey home page Code Monkey logo

react-native-scroll-into-view's Introduction

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.

react-native-scroll-into-view's People

Contributors

berekhalfhand avatar darshanksexathought avatar dependabot[bot] avatar frw avatar slorber avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-scroll-into-view's Issues

Component won't scroll to the top edge of the ScrollView

Hi, I'm not able to get the required component to scroll into view such that it's top edge touches the top edge of the ScrollView. Right now it only scrolls to the bottom edge of the required component. I'm using the enabled prop to decide which component scrolls into view. I tried using insets: { top: 0 } but it doesn't work.
Thanks a lot for creating this package btw!

Using with Flatlist

I have a setup with KeyboardAwareScrollView but for some screens, I use KeyboardAwareFlatList in order to avoid VirtualizedLists should never be nested inside plain ScrollViews with the same orientation.

I would like to do something like:

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

// ScrollIntoView API is not provided in React context. Make sure you wrapped your ScrollView with ScrollIntoViewWrapper
const ScrollIntoViewFlatList = wrapScrollViewConfigured({
  getScrollViewNode: ref => ref,
  refPropName: 'innerRef',
})(KeyboardAwareFlatList);

Has anyone achieved this without changing lots of the package?

Non TS examples

can you make an examples like the normal code in React?
Sorry beginner here, I don't understand how this TS works

I need to the Sections example.

no works on react native 0.66.1

i'm following your example, and then updated react native on the last version, the scroll dosent work.

TypeError: scrollView.getScrollResponder().scrollResponderScrollTo is not a function. (In 'scrollView.getScrollResponder().scrollResponderScrollTo({ x: 0, y: newScrollY, animated: animated })', 'scrollView.getScrollResponder().scrollResponderScrollTo' is undefined)

Checking if the element is already in view?

Use case:

There is a fixed button on bototm of the screen that scrolls into a View when pressed on. However, I want to hide that button if the element has already been scrolled to manually. Is there a way to see if my element is already at the current scroll position?

My code:

<CustomScrollView>
  <ScrollIntoView>
    <View> {/* MyElement */}
      ...
    </View>
  </ScrollIntoView>
</CustomScrollView>
<FloatingButton hidden={/* I want to check here if MyElement is already scrolled to}>Scroll to View</FloatingButton>

Thanks

Highlight component scrolledto

Hey there!

Awesome component, really easy to use!

I just have a small extra feature that I need which I thought others would need too. Is there anyway to highlight the component after the scroll happened?

This need came from the fact that my user is coming from another screen and he has been scrolled to a list of elements, and I want to make it even clearer which item of that list he has been scrolled to!

Warpping my form inside wrapScrollView causes my forminputs to get blurred

I am using Formik to build my forms.
First I wrapped react-native's ScrollView component like so:

  const CustomScrollView = wrapScrollView(ScrollView);

and then I proceed to wrap my entire form inside the above CustomScrollView like so:

<CustomScrollView>
// ... rest of the form code
</CustomScrollView>

As I type two letters into the first form input the input goes out of focus and the keyboard disappears.
My hunch is that it tries to scroll up while typing

Note that this happens even if I don't wrap the input components inside <ScrollIntoView>

Listen to where user currently is

This is a great library thanks a lot, but wouldn't this be practical to get current view that user got to?
That way we can build tabs for scrolling for example and user scrolls manually then the tab index can change automatically.

Sections array with strings doesn't work

@slorber
I am trying to create three buttons with Text like
const Sections = ['OVERVIEW','MODELS','GALLERY']

The below code doesnt work:

const Sections = ['OVERVIEW','MODELS','GALLERY']
    const sectionsRefs = (link: http://Sections.map) Sections.map(
        _section => React.createRef<any>()
    );

    const scrollSectionIntoView = (section: any) => {
      sectionsRefs[section].current!.scrollIntoView({ align: 'top' });
    };

But the below code works:

 const Sections = ['0','1','2'];

    const sectionsRefs = (link: http://Sections.map) Sections.map(
        _section => React.createRef<any>()
    );

    const scrollSectionIntoView = (section: any) => {
      sectionsRefs[section].current!.scrollIntoView({ align: 'top' });
    };

Not working in Android

tried implementing the basic scenario in android , the basic example is not working it always stays in the top , I tried with different items when we call scrollIntoView(viewRef.current) it will take us to top of the screen instead the the ref that we provided it works perfectly fine in IOS but not in android
parent component
const CustomScrollView = wrapScrollView(ScrollView); <CustomScrollView style={styles.scrollView}> <SignupScreen /> </CustomScrollView>

chile component SignupScreen

const scrollIntoView = useScrollIntoView();
const viewRef = useRef(null)

return <>
       <Button title="Scroll a view ref into view" onPress={() => scrollIntoView(viewRef.current)}/>
         
       
 
       <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>
     </>

Having the same item scrolled into view after user scrolled manually.

Hi, first of all thank you for the great lib!

I am having some issues with it,
I have something like this:

<Root>
  <CustomScrollView> /* (align top) */
    <ScrollIntoView enabled={isAEnabled}>
      <A>
    </ScrollIntoView>
    ...
  </CustomScrollView>
  <Button onPress={setANotEnabled} />
  <Button onPress={setAEnabled} />
</Root>

After scrolling item A into view, aka, setting isAEnabled to true, and then user scrolls around manually, and say at some point isAEnabled is set to false, and then set to true again..
A is not scrolling into view.

  • I will state that if there is a Component B that is scrolled into view and then A is requested to scroll into view afterwards.. it works perfect.

Help?

Issue on Orientation

react Scroll into view --> View --> Open Modal component --> rotate screen
re renders the App without Modal

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.