Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion fixture/react-native/src/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,13 @@ const Carousel = () => {
useEffect(() => {
if (isLayoutCompleteRef.current) {
const targetIndex = lastVisibleIndexRef.current;
setTimeout(() => {
const timeout = setTimeout(() => {
flatListRef.current?.scrollToIndex({
index: targetIndex,
animated: false,
});
}, 0);
return () => clearTimeout(timeout);
}
}, [screenWidth]);

Expand Down
38 changes: 25 additions & 13 deletions fixture/react-native/src/CellRendererExamples.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useCallback } from "react";
import React, { forwardRef, useLayoutEffect, useRef, useCallback } from "react";
import { View, Text, StyleSheet, Animated } from "react-native";
import { FlashList } from "@shopify/flash-list";

Expand All @@ -7,57 +7,69 @@ import Tweet from "./twitter/models/Tweet";
import TweetCell from "./twitter/TweetCell";

// Example 1: Fade-in animation CellRendererComponent
const FadeInCellRenderer = (props: any) => {
const FadeInCellRenderer = forwardRef<View, any>((props, ref) => {
const opacity = useRef(new Animated.Value(0)).current;
const cellStyle = StyleSheet.flatten(props.style);

useEffect(() => {
Animated.timing(opacity, {
useLayoutEffect(() => {
opacity.setValue(0);
const animation = Animated.timing(opacity, {
toValue: 1,
duration: 500,
delay: props.index * 100,
useNativeDriver: true,
}).start();
});
animation.start();
return () => animation.stop();
}, [opacity, props.index]);

return (
<Animated.View
{...props}
ref={ref}
style={[
props.style,
{
opacity,
opacity: cellStyle?.opacity === 0 ? 0 : opacity,
},
]}
/>
);
};
});
FadeInCellRenderer.displayName = "FadeInCellRenderer";

// Example 2: Scale animation CellRendererComponent
const ScaleCellRenderer = (props: any) => {
const ScaleCellRenderer = forwardRef<View, any>((props, ref) => {
const scale = useRef(new Animated.Value(0.8)).current;
const cellStyle = StyleSheet.flatten(props.style);

useEffect(() => {
Animated.spring(scale, {
useLayoutEffect(() => {
scale.setValue(0.8);
const animation = Animated.spring(scale, {
toValue: 1,
friction: 8,
tension: 40,
delay: props.index * 50,
useNativeDriver: true,
}).start();
});
animation.start();
return () => animation.stop();
}, [scale, props.index]);

return (
<Animated.View
{...props}
ref={ref}
style={[
props.style,
{
transform: [{ scale }],
transform: [...(cellStyle?.transform ?? []), { scale }],
},
]}
/>
);
};
});
ScaleCellRenderer.displayName = "ScaleCellRenderer";

// Main component that combines both examples
const FlashListCellRenderer = () => {
Expand Down
29 changes: 25 additions & 4 deletions fixture/react-native/src/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
Use this component inside your React Native Application.
A scrollable list with different item type
*/
import React, { useCallback, useRef, useState } from "react";
import React, {
forwardRef,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import { View, Text, Pressable, StyleSheet } from "react-native";
import { FlashList, FlashListRef } from "@shopify/flash-list";
import Animated, {
Expand All @@ -24,6 +30,17 @@ const List = () => {
const [data, setData] = useState(() => generateArray(100));

const list = useRef<FlashListRef<number> | null>(null);
const refreshTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);

useEffect(
() => () => {
if (refreshTimeout.current !== null) {
clearTimeout(refreshTimeout.current);
refreshTimeout.current = null;
}
},
[]
);

const removeItem = useCallback((item: number) => {
list.current?.prepareForLayoutAnimationRender();
Expand Down Expand Up @@ -65,8 +82,10 @@ const List = () => {
ref={list}
refreshing={refreshing}
onRefresh={() => {
if (refreshTimeout.current !== null) return;
setRefreshing(true);
setTimeout(() => {
refreshTimeout.current = setTimeout(() => {
refreshTimeout.current = null;
setRefreshing(false);
}, 2000);
}}
Expand All @@ -85,16 +104,18 @@ const List = () => {

export default List;

const CellRenderer = (props: any) => {
const CellRenderer = forwardRef<View, any>((props, ref) => {
return (
<Animated.View
{...props}
ref={ref}
layout={LinearTransition}
entering={FadeIn}
exiting={FadeOut}
/>
);
};
});
CellRenderer.displayName = "CellRenderer";
const styles = StyleSheet.create({
container: {
justifyContent: "space-around",
Expand Down
10 changes: 5 additions & 5 deletions fixture/react-native/src/components/RecyclingImage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo, useRef } from "react";
import React, { useLayoutEffect, useRef } from "react";
import { Animated, Image, ImageProps, Platform } from "react-native";

interface RecyclingImageProps extends Omit<ImageProps, "source"> {
Expand All @@ -9,17 +9,17 @@ const isIOS = Platform.OS === "ios";
const RecyclingImageIOS = (props: RecyclingImageProps) => {
const animatedOpacity = useRef(new Animated.Value(0)).current;

useMemo(() => {
useLayoutEffect(() => {
animatedOpacity.setValue(0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.source.uri]);
}, [animatedOpacity, props.source.uri]);

return (
<Animated.Image
{...props}
style={[props.style, { opacity: animatedOpacity }]}
onLoad={() => {
onLoad={(event) => {
animatedOpacity.setValue(1);
props.onLoad?.(event);
}}
/>
);
Expand Down
Loading