Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blank FlashList after refresh and switch between tab #20

Open
kirillzyusko opened this issue Nov 28, 2023 · 2 comments
Open

Blank FlashList after refresh and switch between tab #20

kirillzyusko opened this issue Nov 28, 2023 · 2 comments

Comments

@kirillzyusko
Copy link

Steps to reproduce

  • scroll LIKE tab (to ~20 element)
  • go to OWNER tab and pull-to-refresh
  • go back to LIKE tab

Actual behavior

Content is not visible.

Expected behavior

Content should be visible.

Code to reproduce

import React, { useCallback, useState } from "react";
import { StatusBar, Text, View } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import { Route, TabView } from "@showtime-xyz/tab-view";
import { TabFlashList } from "@features/Social/components/TabFlashList";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { TabFlashList } from "./tab-flash-list";

const StatusBarHeight = StatusBar.currentHeight ?? 0;
const TabScene = ({ route }: any) => {
  return (
    <TabFlashList
      index={route.index}
      data={new Array(60).fill(0)}
      estimatedItemSize={79}
      renderItem={({ index }) => {
        return (
          <View
            style={{
              height: 71,
              backgroundColor: "#fff",
              marginBottom: 8,
              justifyContent: "center",
              alignItems: "center",
            }}
          >
            <Text>{`${route.title}-Item-${index}`}</Text>
          </View>
        );
      }}
    />
  );
};

export default function Example() {
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [routes] = useState<Route[]>([
    { key: "like", title: "Like", index: 0 },
    { key: "owner", title: "Owner", index: 1 },
    { key: "created", title: "Created", index: 2 },
  ]);
  const [index, setIndex] = useState(0);
  const animationHeaderPosition = useSharedValue(0);
  const animationHeaderHeight = useSharedValue(0);

  const renderScene = useCallback(({ route }: any) => {
    switch (route.key) {
      case "like":
        return <TabScene route={route} index={0} />;
      case "owner":
        return <TabScene route={route} index={1} />;
      case "created":
        return <TabScene route={route} index={2} />;
      default:
        return null;
    }
  }, []);

  const onStartRefresh = async () => {
    setIsRefreshing(true);
    setTimeout(() => {
      console.log("onStartRefresh");
      setIsRefreshing(false);
    }, 300);
  };
  const renderHeader = () => (
    <View style={{ height: 300, backgroundColor: "#000" }}></View>
  );
  return (
    <GestureHandlerRootView style={{flex: 1}}>
      <TabView
        onStartRefresh={onStartRefresh}
        isRefreshing={isRefreshing}
        navigationState={{ index, routes }}
        renderScene={renderScene}
        onIndexChange={setIndex}
        lazy
        renderScrollHeader={renderHeader}
        minHeaderHeight={44 + StatusBarHeight}
        animationHeaderPosition={animationHeaderPosition}
        animationHeaderHeight={animationHeaderHeight}
      />
    </GestureHandlerRootView>
  );
}

Screenshots:

repro-proof.mp4

Environment info:

  • Pixel 7 Pro (Android 14);
  • "react-native-reanimated": "3.5.4"
  • "react-native-gesture-handler": "^2.13.4"
  • "@shopify/flash-list": "^1.6.2"
  • "react-native": "0.72.7"
@kirillzyusko
Copy link
Author

The problem was fixed by these changes:

     onScroll: (e) => {
       const moveY = e.contentOffset.y;
       scrollY.value = moveY;
-      if (curIndexValue.value !== index) return;
-      shareAnimatedValue.value = moveY;
       if (propOnScroll) {
         runOnJS(propOnScroll as any)({ nativeEvent: e });
       }
+      if (curIndexValue.value !== index) return;
+      shareAnimatedValue.value = moveY;
     },
   });
   // adjust the scene size

It seems like runOnJS(propOnScroll as any)({ nativeEvent: e }); wasn't called when the scene was not in focus. So literally we were scrolling the scene to the initial position (scrollTo from REA), but we didn't propagate this event to JS. As a result FlashList had old offset and was rendering incorrect elements.

Moving runOnJS(propOnScroll as any)({ nativeEvent: e }); before if (curIndexValue.value !== index) return; seems to resolve this problem 👀

@alantoa
Copy link
Collaborator

alantoa commented Feb 20, 2024

Hey, @kirillzyusko, sorry for replying late. I'm not actively maintaining this library because I find the current implementation messy. I always think about how to make this library run perfectly and be easy to use instead of passing an index to the scene view.
Also, I have left Showtime. Next, I'm planning to create a new library and refactor it to make it more composable. And this time I will no longer use react-native-tab-view. I will let you know when it's ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants