Search code examples
javascriptreact-nativereact-native-animatable

React Native Redash onScrollEvent not changing Y value if used in two components


In my current app the animated y value only changes after I hot reload my app.

Before the reload the app's y value of my component doesn't change or atleast it doesnt interpolate the y value

I'm unsure what causes this strange ocurrance.

Here is a snippet of the code.

Here I create the animated Y value

import React from "react";
import { View, StyleSheet } from "react-native";
import Animated from "react-native-reanimated";
import ListHeader from "./ListHeader";
import ListBody from "./ListBody";

const { Value } = Animated;

 const TransitionList = ({ album }) => {
  const y = new Value(0);

  return (
    <View style={styles.container}>
       <ListHeader {...{ y, album }} />
      <ListBody {...{ y, album }} />
    </View>
  );
};

export default TransitionList

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "black",
  },
});

Here I change the Y value onScroll

import React from "react";
import styled from "styled-components";
import { StyleSheet, View } from "react-native";
import { onScrollEvent } from "react-native-redash";
import { LinearGradient } from "expo-linear-gradient";
import Animated from "react-native-reanimated";
import ListTitle from "./ListTitle";
import ListItem from "./ListItem";
import {
  MAX_HEADER_HEIGHT,
  MIN_HEADER_HEIGHT,
  BUTTON_HEIGHT,
} from "../helpers";

const { interpolate, Extrapolate } = Animated;

const ListBody = ({ album: { artist, tracks }, y }) => {
  const height = interpolate(y, {
    inputRange: [-MAX_HEADER_HEIGHT, -BUTTON_HEIGHT / 2],
    outputRange: [0, MAX_HEADER_HEIGHT + BUTTON_HEIGHT],
    extrapolate: Extrapolate.CLAMP,
  });
  const opacity = interpolate(y, {
    inputRange: [-MAX_HEADER_HEIGHT / 2, 0, MAX_HEADER_HEIGHT / 2],
    outputRange: [0, 1, 0],
    extrapolate: Extrapolate.CLAMP,
  });
  return (
    <Animated.ScrollView
      onScroll={onScrollEvent({ y })}
      style={styles.container}
      showsVerticalScrollIndicator={false}
      scrollEventThrottle={1} 
      stickyHeaderIndices={[1]}
    >
      <View style={styles.cover}>
        <Animated.View style={[styles.gradient, { height }]}>
          <LinearGradient
            style={StyleSheet.absoluteFill}
            start={[0, 0.3]}
            end={[0, 1]}
            colors={["transparent", "rgba(0, 0, 0, 0.2)", "black"]}
          />
        </Animated.View>
        <View style={styles.artistContainer}>
          <Animated.Text style={[styles.artist, { opacity }]}>
            {artist}
          </Animated.Text>
        </View>
      </View>
      <View style={styles.header}>
        <ListTitle {...{ y, artist }} />
      </View>
      <View style={styles.tracks}>
        {tracks.map((track, key) =>
          <ListItem index={key + 1} {...{ track, key, artist }} />
        )}
      </View>
    </Animated.ScrollView>
  );
};

export default ListBody


const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: MIN_HEADER_HEIGHT - BUTTON_HEIGHT / 2,
  },
  cover: {
    height: MAX_HEADER_HEIGHT - BUTTON_HEIGHT,
  },
  gradient: {
    position: "absolute",
    left: 0,
    bottom: 0,
    right: 0,
    alignItems: "center",
  },
  artistContainer: {
    ...StyleSheet.absoluteFillObject,
    justifyContent: "center",
    alignItems: "center",
  },
  artist: {
    textAlign: "center",
    color: "white",
    fontSize: 48,
    fontWeight: "bold",
  },
  header: {
    marginTop: -BUTTON_HEIGHT,
  },
  tracks: {
    paddingTop: 32,
    backgroundColor: "black",
  },
});

Here the Y value's change should also occur in this component

import React from "react";
import Animated from "react-native-reanimated";
import { Image, StyleSheet } from "react-native";

import { MAX_HEADER_HEIGHT, HEADER_DELTA, BUTTON_HEIGHT } from "../helpers";

const { interpolate, Extrapolate } = Animated;

const ListHeader = ({ album: { cover }, y }) => {
  const scale = interpolate(y, {
    inputRange: [-MAX_HEADER_HEIGHT, 0],
    outputRange: [4, 1],
    extrapolateRight: Extrapolate.CLAMP,
  });
  const opacity = interpolate(y, {
    inputRange: [-64, 0, HEADER_DELTA],
    outputRange: [0, 0.2, 1],
    extrapolate: Extrapolate.CLAMP,
  });

  return (
    <Animated.View style={[styles.container, { transform: [{ scale }] }]}>
      <Image style={styles.image} source={cover} />
      <Animated.View
        style={{
          ...StyleSheet.absoluteFillObject,
          backgroundColor: "black",
          opacity,
        }}
      />
    </Animated.View>
  );
};

export default ListHeader


const styles = StyleSheet.create({
  container: {
    ...StyleSheet.absoluteFillObject,
    height: MAX_HEADER_HEIGHT + BUTTON_HEIGHT * 2,
  },
  image: {
    ...StyleSheet.absoluteFillObject,
    width: undefined,
    height: undefined,
  },
});


Solution

  • I fixed it by removing the reanimated and react-native-redash libraries and using RN's own Animated library

    Example below:

    const TransitionList = ({
      album,
      children,
      image,
      title,
      sub,
      list,
      header,
    }) => {
      const [scrollY, setScrollY] = useState(new Animated.Value(0));
    
    
      return (
        <Container SCREEN_HEIGHT={SCREEN_HEIGHT} ThemeColors={ThemeColors}>
          <ListHeader y={scrollY} album={album} image={image} />
          <ListBody
            setY={setScrollY}
            y={scrollY}
            album={album}
            title={title}
            sub={sub}
            header={header}
            list={list}
          >
            {children}
          </ListBody>
        </Container>
      );
    };
    
    <Animated.ScrollView
          onScroll={event =>
            setY(new Animated.Value(event.nativeEvent.contentOffset.y))}
          style={styles.container}
          showsVerticalScrollIndicator={false}
          scrollEventThrottle={1}
          stickyHeaderIndices={[1]}
        >