Skip to content

Instantly share code, notes, and snippets.

@mmazzarolo
Last active October 31, 2023 07:32
Show Gist options
  • Star 52 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save mmazzarolo/cfd467436f9d110e94a685b06eb3900f to your computer and use it in GitHub Desktop.
Save mmazzarolo/cfd467436f9d110e94a685b06eb3900f to your computer and use it in GitHub Desktop.
react-native-action-button hide on scroll
// 1. Define a state variable for showing/hiding the action-button
state = {
isActionButtonVisible: true
}
// 2. Define a variable that will keep track of the current scroll position
_listViewOffset = 0
// 3. Add an onScroll listener to your listview/scrollview
<ListView
...
onScroll={this._onScroll}
...
/>
// 3. Add some logic in the scroll listener for hiding the action button when scrolling down
_onScroll = (event) => {
// Simple fade-in / fade-out animation
const CustomLayoutLinear = {
duration: 100,
create: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
update: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
delete: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity }
}
// Check if the user is scrolling up or down by confronting the new scroll position with your own one
const currentOffset = event.nativeEvent.contentOffset.y
const direction = (currentOffset > 0 && currentOffset > this._listViewOffset)
? 'down'
: 'up'
// If the user is scrolling down (and the action-button is still visible) hide it
const isActionButtonVisible = direction === 'up'
if (isActionButtonVisible !== this.state.isActionButtonVisible) {
LayoutAnimation.configureNext(CustomLayoutLinear)
this.setState({ isActionButtonVisible })
}
// Update your scroll position
this._listViewOffset = currentOffset
}
// 4. In you render show you action-button only if state.isActionButtonVisible === true
<View style={styles.container}>
{yourListView}
{this.state.isActionButtonVisible ? <ActionButton /> : null}
</View>
@mmazzarolo
Copy link
Author

It can be tweaked further for handling the bottom "bounce" of the iOS scrollview (the top one is handled by currentOffset > 0.
The tweak might consist in a mix of checking if the bottom of the scrollview is visible and checking the scrollview content height.

@ufon
Copy link

ufon commented Oct 24, 2016

for android put in constructor:
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);

@pewh
Copy link

pewh commented Dec 29, 2016

When I set backdrop props on <ActionButton .. />, it will trigger backdrop every scroll to top. Is it a bug from the component itself?

Uploading asd.gif…

@pewh
Copy link

pewh commented Dec 29, 2016

asd

@ruxiang05
Copy link

I am given this error but I don't really understand what I should do.

You specified onScroll on a but not scrollEventThrottle. You will only receive one event. Using 16 you get all the events but be aware that it may cause frame drops, use a bigger number if you don't need as much precision.

@damikdk
Copy link

damikdk commented Sep 19, 2017

@ruxiang05 this warning is about update frequency on iOS. The less the more often (docs)

@mmazzarolo, thanks for code!

@kkkevinnn
Copy link

I have fixed the bottom bounce issue based on this
https://gist.github.com/hisokakei/25ace65e3df656313990b710354e1541

@rsp8055
Copy link

rsp8055 commented Apr 5, 2018

How to show alphabet bubble in scroll bar indicator

@bezenson
Copy link

On iOS we have bounce effect. So I improved a bit:

    const isBottomBounce =
      event.nativeEvent.layoutMeasurement.height -
        event.nativeEvent.contentSize.height +
        event.nativeEvent.contentOffset.y >=
      0;


    let direction = currentOffset > 0 && currentOffset > this.listViewOffset ? 'down' : 'up';
    if (direction === 'up' && isBottomBounce) {
      direction = 'down';
    }

@hmoule
Copy link

hmoule commented Feb 18, 2020

For those using functional components you could turn it into a hook like this

import { useState, useRef, useCallback } from 'react';
import { NativeSyntheticEvent, NativeScrollEvent, LayoutAnimation } from 'react-native';

const useHandleScroll = () => {
  const [showButton, setShowButton] = useState(true);

  const scrollOffset = useRef(0);

  const handleScroll = useCallback(
    (event: NativeSyntheticEvent<NativeScrollEvent>) => {
      const CustomLayoutLinear = {
        duration: 100,
        create: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
        update: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
        delete: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
      };
      // Check if the user is scrolling up or down by confronting the new scroll position with your own one
      const currentOffset = event.nativeEvent.contentOffset.y;
      const direction = currentOffset > 0 && currentOffset > scrollOffset.current ? 'down' : 'up';
      // If the user is scrolling down (and the action-button is still visible) hide it
      const isActionButtonVisible = direction === 'up';
      if (isActionButtonVisible !== showButton) {
        LayoutAnimation.configureNext(CustomLayoutLinear);
        setShowButton(isActionButtonVisible);
      }
      // Update your scroll position
      scrollOffset.current = currentOffset;
    },
    [showButton]
  );

  return { handleScroll, showButton };
};

export default useHandleScroll;

and then use it like this

const { handleScroll, showButton } = useHandleScroll();

<ScrollView onScroll={handleScroll} />
{showButton && <Button />}

@Madhu02
Copy link

Madhu02 commented Nov 15, 2020

@hmoule

For those using functional components you could turn it into a hook like this

import { useState, useRef, useCallback } from 'react';
import { NativeSyntheticEvent, NativeScrollEvent, LayoutAnimation } from 'react-native';

const useHandleScroll = () => {
  const [showButton, setShowButton] = useState(true);

  const scrollOffset = useRef(0);

  const handleScroll = useCallback(
    (event: NativeSyntheticEvent<NativeScrollEvent>) => {
      const CustomLayoutLinear = {
        duration: 100,
        create: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
        update: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
        delete: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
      };
      // Check if the user is scrolling up or down by confronting the new scroll position with your own one
      const currentOffset = event.nativeEvent.contentOffset.y;
      const direction = currentOffset > 0 && currentOffset > scrollOffset.current ? 'down' : 'up';
      // If the user is scrolling down (and the action-button is still visible) hide it
      const isActionButtonVisible = direction === 'up';
      if (isActionButtonVisible !== showButton) {
        LayoutAnimation.configureNext(CustomLayoutLinear);
        setShowButton(isActionButtonVisible);
      }
      // Update your scroll position
      scrollOffset.current = currentOffset;
    },
    [showButton]
  );

  return { handleScroll, showButton };
};

export default useHandleScroll;

and then use it like this

const { handleScroll, showButton } = useHandleScroll();

<ScrollView onScroll={handleScroll} />
{showButton && <Button />}

Could you please help me in converting the above code into Class Component unable to use the Hook in class.

@risardi123
Copy link

Thanks, @hmoule

@aravi365
Copy link

On iOS we have bounce effect. So I improved a bit:

    const isBottomBounce =
      event.nativeEvent.layoutMeasurement.height -
        event.nativeEvent.contentSize.height +
        event.nativeEvent.contentOffset.y >=
      0;


    let direction = currentOffset > 0 && currentOffset > this.listViewOffset ? 'down' : 'up';
    if (direction === 'up' && isBottomBounce) {
      direction = 'down';
    }

By adding this code with @hmoule 's code i am getting a weird jump effect in android when started scrolling. Any way to avoid that? it works perfectly on iOS

@aravi365
Copy link

@aravi365
Copy link

aravi365 commented Nov 19, 2020

Its run perfect on my android @aravi365
make sure on your scrollview Props looks like this
onScroll={handleScroll.bind(this)}

it happens only when scrolling slowly. Works like normal when scrolled in normal speed. My usecase is not an action button, instead it is a tabbar which autohides on scrolldown. That might be the root cause of this issue? any guesses

@stoura90
Copy link

For those using functional components you could turn it into a hook like this

import { useState, useRef, useCallback } from 'react';
import { NativeSyntheticEvent, NativeScrollEvent, LayoutAnimation } from 'react-native';

const useHandleScroll = () => {
  const [showButton, setShowButton] = useState(true);

  const scrollOffset = useRef(0);

  const handleScroll = useCallback(
    (event: NativeSyntheticEvent<NativeScrollEvent>) => {
      const CustomLayoutLinear = {
        duration: 100,
        create: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
        update: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
        delete: { type: LayoutAnimation.Types.linear, property: LayoutAnimation.Properties.opacity },
      };
      // Check if the user is scrolling up or down by confronting the new scroll position with your own one
      const currentOffset = event.nativeEvent.contentOffset.y;
      const direction = currentOffset > 0 && currentOffset > scrollOffset.current ? 'down' : 'up';
      // If the user is scrolling down (and the action-button is still visible) hide it
      const isActionButtonVisible = direction === 'up';
      if (isActionButtonVisible !== showButton) {
        LayoutAnimation.configureNext(CustomLayoutLinear);
        setShowButton(isActionButtonVisible);
      }
      // Update your scroll position
      scrollOffset.current = currentOffset;
    },
    [showButton]
  );

  return { handleScroll, showButton };
};

export default useHandleScroll;

and then use it like this

const { handleScroll, showButton } = useHandleScroll();

<ScrollView onScroll={handleScroll} />
{showButton && <Button />}

i have a problem
when i use Animated.Scrollview and when i pass the parameter (handleScroll ) some other components in the screen also hid

@Prince-AppsTango
Copy link

thanks

@fukemy
Copy link

fukemy commented Jul 24, 2023

any solution with Reanimated?? I dont want to useState + LayoutAnimation because it's may took glitch with Android

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment