react-native: PLEASE REPORT: Excessive number of pending callbacks: 501

Description

Hi,

I keep the getting the error seen on the screenshot below. For context, I’m creating an app (IOS) where users can swipe through a number of songs to listen to the audio and comment on each song.

This error warning shows when I finish swiping on a song and load the next one. Any help would be great - my app keeps crashing on my physical device (fine on simulator) after a few swipes. Full error shown below.

Version

0.66.1

Output of npx react-native info

System: OS: macOS 12.3.1 CPU: (8) arm64 Apple M1 Memory: 95.30 MB / 16.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 16.16.0 - /usr/local/bin/node Yarn: 1.22.19 - /usr/local/bin/yarn npm: 8.11.0 - /usr/local/bin/npm Watchman: 2022.06.27.00 - /opt/homebrew/bin/watchman Managers: CocoaPods: 1.11.3 - /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5 Android SDK: Not Found IDEs: Android Studio: Not Found Xcode: 13.4/13F17a - /usr/bin/xcodebuild Languages: Java: Not Found npmPackages: @react-native-community/cli: Not Found react: 18.0.0 => 18.0.0 react-native: 0.69.1 => 0.69.1 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found

Steps to reproduce

Swipe through songs to reproduce.

Snack, code example, screenshot, or link to a repository

  StyleSheet,
  Text,
  View,
  Dimensions,
  Image,
  TextInput,
  FlatList,
  TouchableOpacity,
  ScrollView,
  Keyboard,
} from 'react-native';
import React, {useState, useEffect, useRef, useMemo, useCallback} from 'react';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
import {firebase} from '@react-native-firebase/firestore';
import Colors from '../assets/utilities/Colors';
import Spotify from '../assets/img/spotify.svg';
import AsyncStorage from '@react-native-async-storage/async-storage';
import firestore from '@react-native-firebase/firestore';
import storage from '@react-native-firebase/storage';
import Ionicons from 'react-native-vector-icons/Ionicons';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withSpring,
  runOnJS,
} from 'react-native-reanimated';
const {height: SCREEN_HEIGHT} = Dimensions.get('window');

const BottomSheet = props => {
  console.log(SCREEN_HEIGHT);
  const caption = props.captionProps;
  const songID = props.songIDProps;
  const navigation = props.navigationProps;
  const [UID, setUID] = useState();
  const [displayName, setDisplayName] = useState();
  const [profilePicURL, setProfilePicURL] = useState();
  const [inputTop, setInputTop] = useState(false);
  const [commentText, setCommentText] = useState();
  const [parentComments, setParentComments] = useState();
  const [activeLikedComments, setActiveLikedComments] = useState([]);
  const [likeChanged, setLikedChanged] = useState(false);
  const [myComment, setMyComment] = useState(false);
  const [bottomSheetSmall, setBottomSheetSmall] = useState(false);
  const [commentID, setCommentID] = useState();
  const inputRef = useRef();
  const replyUsernameRef = useRef();
  const [replyUsername, setReplyUsername] = useState();
  const [replyActive, setReplyActive] = useState(false);
  const [replyID, setReplyID] = useState();
  const [parentReplies, setParentReplies] = useState();
  const [viewReplies, setViewReplies] = useState(false);
  const [containerUp, setContainerUp] = useState(false);
  const translateY = useSharedValue(0);
  const scrollTo = useCallback(
    destination => {
      'worklet';
      translateY.value = withSpring(destination, {damping: 50});
    },
    [translateY],
  );
  const context = useSharedValue({y: 0});

  const gesture = Gesture.Pan()
    .onStart(() => {
      context.value = {y: translateY.value};
    })
    .onUpdate(event => {
      translateY.value = event.translationY + context.value.y;
      translateY.value = Math.max(translateY.value, -269);
    })
    .onEnd(() => {
      if (!containerUp) {
        if (translateY.value <= -50) {
          scrollTo(-269);
          runOnJS(setContainerUp)(true);
        } else if (translateY.value >= 50) {
          scrollTo(100);
        } else {
          scrollTo(0);
        }
        runOnJS(setBottomSheetSmall)(true);
      } else if (containerUp) {
        if (translateY.value >= -240) {
          scrollTo(0);
          runOnJS(setContainerUp)(false);
        } else {
          scrollTo(0);
        }
        runOnJS(setBottomSheetSmall)(false);
      }
    });

  const rBottomSheetStyle = useAnimatedStyle(() => {
    return {
      transform: [{translateY: translateY.value}],
    };
  });

  useEffect(() => {
    const checkforUID = async () => {
      const userUID = await AsyncStorage.getItem('UID');
      if (userUID) {
        console.log(userUID);
        setUID(userUID);
      }
    };
    checkforUID();
  }, []);

  useEffect(() => {
    if (UID) {
      const getProfilePicURL = async () => {
        const url = await storage()
          .ref(UID + 'PFP')
          .getDownloadURL()
          .catch(error => {
            console.log(error);
            const getDefaultPicURL = async () => {
              const defaultURL = await storage()
                .ref('circle.png')
                .getDownloadURL()
                .catch(error2 => {
                  console.log(error2);
                });
              setProfilePicURL(defaultURL);
              console.log(url);
            };
            getDefaultPicURL();
          });
        setProfilePicURL(url);
        console.log(url);
      };
      getProfilePicURL();

      const getUserProfile = async () => {
        const user = await firestore().collection('users').doc(UID).get();
        setDisplayName(user._data?.displayName);
      };
      getUserProfile();
    }
  }, [UID]);

  useEffect(() => {
    if (UID) {
      const getUserProfile = async () => {
        const user = await firestore().collection('users').doc(UID).get();
        setDisplayName(user._data?.displayName);
      };
      getUserProfile();
    }
  }, [UID]);

  useEffect(() => {
    if (displayName) {
      console.log(displayName);
    }
  }, [displayName]);

  const postComment = () => {
    const currentdate = new Date();
    firestore()
      .collection('posts')
      .doc(songID)
      .collection('comments')
      .add({
        UID: UID,
        parent: 'none',
        comment: commentText,
        profilePicURL: profilePicURL,
        displayName: displayName,
        likeAmount: 0,
        hasReplies: 'no',
        commentAddedAt:
          currentdate.getMonth() +
          1 +
          '/' +
          currentdate.getUTCDate() +
          '/' +
          currentdate.getFullYear() +
          ' @ ' +
          currentdate.getHours() +
          ':' +
          currentdate.getMinutes() +
          ':' +
          currentdate.getSeconds(),
      })
      .then(() => {
        console.log('post added!');
      });
  };

  // get all parent comments
  useEffect(() => {
    if (songID) {
      firestore()
        .collection('posts')
        .doc(songID)
        .collection('comments')
        .where('parent', '==', 'none')
        .orderBy('likeAmount', 'desc')
        .get()
        .then(querySnapshot => {
          console.log(querySnapshot);
          setParentComments(querySnapshot._docs);
        });
      //re-run this effect everytime a user posts a comment
      setMyComment(false);
    }
  }, [songID, myComment]);

  // useEffect(() => {
  //   if (songID) {
  //     firestore()
  //       .collection('posts')
  //       .doc(songID)
  //       .collection('comments')
  //       .doc('ttttttttttttttttttttttt')
  //       .get()
  //       .then(querySnapshot => {
  //         console.log(querySnapshot);
  //         setParentComments(querySnapshot._docs);
  //       });
  //     //re-run this effect everytime a user posts a comment
  //     setMyComment(false);
  //   }
  // }, []);

  // Like a comment logic
  useEffect(() => {
    const increment = firebase.firestore.FieldValue.increment(1);
    const minusIncrement = firebase.firestore.FieldValue.increment(-1);
    if (commentID) {
      if (activeLikedComments.includes(commentID)) {
        setActiveLikedComments(
          activeLikedComments.filter(comment => comment !== commentID),
        );
        firestore()
          .collection('posts')
          .doc(songID)
          .collection('comments')
          .doc(commentID)
          .update({
            likeAmount: minusIncrement,
          });
      } else {
        setActiveLikedComments(current => [...current, commentID]);
        try {
          firestore()
            .collection('posts')
            .doc(songID)
            .collection('comments')
            .doc(commentID)
            .update({
              likeAmount: increment,
            });
        } catch (error) {
          console.log(error);
        }
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [likeChanged, commentID]);

  useEffect(() => {
    if (activeLikedComments) {
      console.log(activeLikedComments);
    }
  }, [activeLikedComments]);

  // REPLY LOGIC BELOW
  const postReply = () => {
    const currentdate = new Date();
    firestore()
      .collection('posts')
      .doc(songID)
      .collection('comments')
      .add({
        UID: UID,
        parent: replyID,
        comment: commentText,
        profilePicURL: profilePicURL,
        displayName: displayName,
        likeAmount: 0,
        commentAddedAt:
          currentdate.getMonth() +
          1 +
          '/' +
          currentdate.getUTCDate() +
          '/' +
          currentdate.getFullYear() +
          ' @ ' +
          currentdate.getHours() +
          ':' +
          currentdate.getMinutes() +
          ':' +
          currentdate.getSeconds(),
      })
      .then(() => {
        console.log('post added!');
      });
    firestore()
      .collection('posts')
      .doc(songID)
      .collection('comments')
      .doc(replyID)
      .update({
        hasReplies: 'yes',
      })
      .then(() => {
        console.log('post added!');
      });
  };

  const commentHandler = () => {
    if (replyActive) {
      postReply();
      console.log('reply is true');
      setReplyActive(false);
    } else {
      postComment();
    }
  };

  useEffect(() => {
    if (replyID) {
      firestore()
        .collection('posts')
        .doc(songID)
        .collection('comments')
        .where('parent', '==', replyID)
        .orderBy('likeAmount', 'desc')
        .get()
        .then(querySnapshot => {
          console.log(querySnapshot);
          setParentReplies(querySnapshot);
        });
      //re-run this effect everytime a user posts a comment
      setMyComment(false);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [viewReplies, myComment]);

  useEffect(() => {
    if (parentReplies) {
      console.log(parentReplies?._docs[0]?._data?.parent);
    } else {
      console.log('not true');
    }
  }, [parentReplies]);

  return (
    <>
      <GestureDetector gesture={gesture}>
        <Animated.View
          style={[styles.commentContainerBackground, rBottomSheetStyle]}>
          <View style={styles.drawer} />
          {caption && (
            <View style={styles.captionContainer}>
              <View style={styles.userContainer}>
                <Spotify height={15} width={15} />
                <Text style={styles.username}>username</Text>
              </View>
              <View style={styles.captionTextContainer}>
                <Text style={styles.caption}>{caption}</Text>
              </View>
            </View>
          )}

          {parentComments && (
            <FlatList
              // style={styles.commentFlatList}
              // contentContainerStyle={{paddingBottom: '200%'}}
              contentContainerStyle={
                bottomSheetSmall
                  ? {paddingBottom: '110%'}
                  : {paddingBottom: '160%'}
              }
              data={parentComments}
              renderItem={({item, index}) => {
                return (
                  <>
                    <View key={index} style={styles.mainContainer}>
                      <View style={styles.commentContainer}>
                        <View style={styles.commentLeftSide}>
                          <TouchableOpacity
                            onPress={() =>
                              navigation.navigate('ViewUserScreen', {
                                UID: item._data.UID,
                                myUID: UID,
                              })
                            }>
                            <Image
                              style={styles.userProfilePic}
                              source={{
                                uri: item._data.profilePicURL,
                              }}
                            />
                          </TouchableOpacity>
                          <View style={styles.commentTextContainer}>
                            <TouchableOpacity
                              onPress={() =>
                                navigation.navigate('ViewUserScreen', {
                                  UID: item._data.UID,
                                  myUID: UID,
                                })
                              }>
                              <Text
                                ref={replyUsernameRef}
                                style={styles.userDisplayName}>
                                {item._data.displayName}
                              </Text>
                            </TouchableOpacity>
                            <Text style={styles.userComment}>
                              {item._data.comment}
                            </Text>
                          </View>
                        </View>
                        <View style={styles.likesContainer}>
                          <TouchableOpacity
                            onPress={() => {
                              setCommentID(item.id);
                              setMyComment(!myComment);
                              setLikedChanged(!likeChanged);
                            }}>
                            <Ionicons
                              style={styles.socialIcon}
                              name={
                                activeLikedComments.includes(item.id)
                                  ? 'heart'
                                  : 'heart-outline'
                              }
                              color={
                                activeLikedComments.includes(item.id)
                                  ? Colors.red
                                  : 'grey'
                              }
                              size={18}
                            />
                          </TouchableOpacity>
                          <Text style={styles.likeText}>
                            {item._data.likeAmount}
                          </Text>
                        </View>
                      </View>
                      <TouchableOpacity
                        onPress={() => {
                          setReplyActive(!replyActive);
                          inputRef.current.focus();
                          setReplyUsername(item._data.displayName);
                          setReplyID(item.id);
                        }}
                        style={styles.replyContainer}>
                        <Text style={styles.replyText}>Reply</Text>
                      </TouchableOpacity>
                      {item._data.hasReplies === 'yes' && (
                        <TouchableOpacity
                          style={styles.viewRepliesContainer}
                          onPress={() => {
                            setReplyID(item.id);
                            setViewReplies(!viewReplies);
                          }}>
                          <Text style={styles.viewRepliesText}>
                            View Replies
                          </Text>
                          <Ionicons
                            // style={styles.socialIcon}
                            name={viewReplies ? 'chevron-up' : 'chevron-down'}
                            color={'grey'}
                            size={18}
                          />
                        </TouchableOpacity>
                      )}
                      {parentReplies &&
                      item.id === parentReplies._docs[0]._data?.parent &&
                      viewReplies ? (
                        <>
                          {parentReplies._docs.map(reply => {
                            return (
                              <View
                                key={reply._data.id}
                                style={styles.repliesContainer}>
                                <View style={styles.repliesLeftSide}>
                                  <Image
                                    style={styles.repliesProfilePic}
                                    source={{
                                      uri: reply._data.profilePicURL,
                                    }}
                                  />
                                  <View style={styles.repliesTextContainer}>
                                    <Text
                                      ref={replyUsernameRef}
                                      style={styles.repliesDisplayName}>
                                      {reply._data.displayName}
                                    </Text>
                                    <Text style={styles.repliesComment}>
                                      {reply._data.comment}
                                    </Text>
                                  </View>
                                </View>
                                <View style={styles.likesContainer}>
                                  <TouchableOpacity
                                    onPress={() => {
                                      setCommentID(reply.id);
                                      setMyComment(!myComment);
                                      setLikedChanged(!likeChanged);
                                    }}>
                                    <Ionicons
                                      style={styles.socialIcon}
                                      name={
                                        activeLikedComments.includes(reply.id)
                                          ? 'heart'
                                          : 'heart-outline'
                                      }
                                      color={
                                        activeLikedComments.includes(reply.id)
                                          ? Colors.red
                                          : 'grey'
                                      }
                                      size={18}
                                    />
                                  </TouchableOpacity>
                                  <Text style={styles.likeText}>
                                    {reply._data.likeAmount}
                                  </Text>
                                </View>
                              </View>
                            );
                          })}
                        </>
                      ) : null}
                    </View>
                  </>
                );
              }}
            />
          )}

Simulator Screen Shot - iPhone 13 - 2022-08-17 at 17 38 14

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 18 (1 by maintainers)

Commits related to this issue

Most upvoted comments

I just ran into this same issue. I tracked it down to the TouchableOpacities in a few components and replaced them with Pressables, and that got rid of the error. I think the “NativeAnimatedModule” is referring to the animation on the touchableOpacity components

Yeah this seems to be caused by a change to TouchableOpacity in React Native 0.69…

https://github.com/facebook/react-native/commit/3eddc9abb70eb54209c68aab7dbd69e363cc7b29

(flattenStyle(prevProps.style)?.opacity !==
        flattenStyle(this.props.style)?.opacity) !==
        undefined

is always going to evaluate to true, therefore calling this._opacityInactive(250); as many times as the component is (re)rendered.

Quick fix: remove the !== undefined from node_modules/react-native/Libraries/Components/Touchable/TouchableOpacity.js then run npx patch-package react-native

I got the same issue in 70.5 even though It has @Girum’s fix on it. I had to change TouchcableOpacity to Pressable to solve it.

Yeah this seems to be caused by a change to TouchableOpacity in React Native 0.69…

3eddc9a

(flattenStyle(prevProps.style)?.opacity !==
        flattenStyle(this.props.style)?.opacity) !==
        undefined

is always going to evaluate to true, therefore calling this._opacityInactive(250); as many times as the component is (re)rendered.

Quick fix: remove the !== undefined from node_modules/react-native/Libraries/Components/Touchable/TouchableOpacity.js then run npx patch-package react-native

It saved my life. Thanks!

Changing all my TouchableOpacity to Pressable worked for me too. We thought it was to do with a bunch of promises/async awaits.

For anyone interested you can create a custom Pressable component in Typescript that provides the same visual feedback that TouchableOpacity did and import and swap this wherever you used TouchableOpacity and not have to style each Pressable.

import React from 'react';
import {
  Pressable as RNPressable,
  PressableProps as RNPressableProps,
} from 'react-native';

type PressableProps = RNPressableProps & {
  pressedStyle?: object;
};

const Pressable: React.FC<PressableProps> = ({
  children,
  style,
  pressedStyle,
  ...props
}) => {
  const defaultPressedStyle = {opacity: 0.2}; // default TouchableOpacity-like press style
  const combinedPressedStyle = pressedStyle || defaultPressedStyle;

  return (
    <RNPressable
      style={({pressed}) => [style, pressed ? combinedPressedStyle : null]}
      {...props}>
      {children}
    </RNPressable>
  );
};

export default Pressable;

I change touchOpacity to Pressable, but it is not work for me.

I just ran into this same issue. I tracked it down to the TouchableOpacities in a few components and replaced them with Pressables, and that got rid of the error. I think the “NativeAnimatedModule” is referring to the animation on the touchableOpacity components

Also encountered this using TouchableOpacity, might try to use Pressable instead.

Update:

Using Pressable instead of TouchableOpacity removes the warning for now. Hopefully, it won’t come back. 😂 await is also a plus.

That is a busy component 🙃 i don’t have a solution for you but I run into similar issues. Did you find any solution?

Changing all my TouchableOpacity to Pressable worked for me too. We thought it was to do with a bunch of promises/async awaits.

For anyone interested you can create a custom Pressable component in Typescript that provides the same visual feedback that TouchableOpacity did and import and swap this wherever you used TouchableOpacity and not have to style each Pressable.

import React from 'react';
import {
  Pressable as RNPressable,
  PressableProps as RNPressableProps,
} from 'react-native';

type PressableProps = RNPressableProps & {
  pressedStyle?: object;
};

const Pressable: React.FC<PressableProps> = ({
  children,
  style,
  pressedStyle,
  ...props
}) => {
  const defaultPressedStyle = {opacity: 0.2}; // default TouchableOpacity-like press style
  const combinedPressedStyle = pressedStyle || defaultPressedStyle;

  return (
    <RNPressable
      style={({pressed}) => [style, pressed ? combinedPressedStyle : null]}
      {...props}>
      {children}
    </RNPressable>
  );
};

export default Pressable;

Thanks, solved the error for me too

@Svarto this component was being rendered inside another flatlist in another component. I just took it out of the flatlist and it worked fine. Just be careful of a lot of animated code being fired at once.