'React native randomly crashes with error 'Attempted to remove more RCTKeyboardObserver listeners than added'
My react native project on iOS simulator crashes randomly with this error when I open and close the ChatRoom screen, I guess it's a problem with the react native gifted chat library I'm not sure, that's the only external library I use in that screen.
I have tried Keyboard remove listener solution Keyboard.addListener("keyboardWillShow").remove();
from https://github.com/rgommezz/react-native-offline/issues/55 in my App.js file but it don't work.
I have added the code of the screen where this crash randomly occurs. Project versions are "react": "17.0.2", "react-native": "0.66.4", "react-native-gifted-chat": "^0.16.3".
ChatRoom
const ChatRoom = ({ navigation, route }) => {
React.useLayoutEffect(() => {
navigation.setOptions({
title: name,
});
}, [navigation, name]);
React.useEffect(() => {
mounted.current = true;
async function fetch() {
const response = await getUserData(receiverId);
if (mounted.current) {
setChatImage(response.userimg);
setName(response.name);
}
const chat = await getChat(user.uid, userid);
if (chat) {
const unsubscribe = firestore()
.collection("Messages")
.where("chatid", "==", chat)
.onSnapshot((querySnapshot) => {
const messages = querySnapshot
.docChanges()
.filter(({ type }) => type === "added")
.map(({ doc }) => {
const message = doc.data();
return {
...message,
createdAt: message.createdAt.toDate(),
};
})
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
appendMessages(messages);
});
return () => unsubscribe();
}
}
fetch();
return () => (mounted.current = false);
}, []);
React.useEffect(() => {
// set message state
if (receiverId) {
if (user.uid !== receiverId) {
async function fetch() {
const chat = await getChat(user.uid, userid);
await markMsgStatus(user.uid, chat);
}
fetch();
}
}
}, [receiverId]);
const appendMessages = React.useCallback(
(messages) => {
setMessages((previousMessages) =>
GiftedChat.append(previousMessages, messages)
);
},
[messages]
);
const handleSend = async (messages) => {
// if there are no chats, create one
const chat = await getChat(user.uid, userid);
let chatid;
if (!chat) {
chatid = await createChat(user.uid, userid);
}
// increment the no of messages in the chat by one
firestore()
.collection("Chats")
.doc(chat ? chat : chatid)
.set(
{
noofmsgs: firestore.FieldValue.increment(1),
},
{ merge: true }
)
.then(() => console.log("message count on chat increased by one"))
.catch((e) => console.log(e));
// add the messages
const writes = messages.map((m) =>
firestore()
.collection("Messages")
.add({
...m,
sent: true,
received: false,
senderid: user.uid,
receiverid: userid,
chatid: chat ? chat : chatid,
})
);
await Promise.all(writes);
// add the latest msg to chat
const writetochat = messages.find((m) =>
firestore()
.collection("Chats")
.doc(chat ? chat : chatid)
.set(
{
latestmsg: m,
latestmsgsender: user.uid,
latestmsgreceiver: userid,
},
{ merge: true }
)
);
await Promise.all(writetochat);
};
return (
<View>
<GiftedChat
messages={messages}
user={{
_id: user.uid,
}}
onSend={handleSend}
showAvatarForEveryMessage={true}
/>
</View>
);
};
export default ChatRoom;
Solution 1:[1]
Having the same issue and a kinda crash on ios when unmounting the component I was not able to make AkShil's solution work. I found this tutorial which shows a similar solution.
For me it worked like this :
- npm install patch-package -D
- Change the root package.json script section and add "postinstall": "patch-package"
- Change the file node_modules/react-native-gifted-chat/lib/MessageContainer.js according to AkShil's solution
- npx patch-package react-native-gifted-chat
This creates a folder /patches in the root directory and updates automatically the node_modules' packages when you npm install. Works like charm.
PS: to ios simulator users just check and uncheck I/O -> Keyboard if it does not show anymore
Solution 2:[2]
This is the issue of the package itself. Below is how I fixed it.
- Install package
npm i patch-package
- Create a Folder called
patches
on the root of the directory - Create a new file name
patch-rn-gifted-chat
within patch folder - add below code to it
diff --git a/node_modules/react-native-gifted-chat/lib/MessageContainer.js b/node_modules/react-native-gifted-chat/lib/MessageContainer.js
index 193772a..4e80378 100644
--- a/node_modules/react-native-gifted-chat/lib/MessageContainer.js
+++ b/node_modules/react-native-gifted-chat/lib/MessageContainer.js
@@ -55,18 +55,18 @@ export default class MessageContainer extends React.PureComponent {
this.attachKeyboardListeners = () => {
const { invertibleScrollViewProps: invertibleProps } = this.props;
if (invertibleProps) {
- Keyboard.addListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
- Keyboard.addListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
- Keyboard.addListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
- Keyboard.addListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
+ this.willShowSub = Keyboard.addListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
+ this.didShowSub = Keyboard.addListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
+ this.willHideSub = Keyboard.addListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
+ this.didHideSub = Keyboard.addListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
}
};
this.detachKeyboardListeners = () => {
const { invertibleScrollViewProps: invertibleProps } = this.props;
- Keyboard.removeListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
- Keyboard.removeListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
- Keyboard.removeListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
- Keyboard.removeListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
+ this.willShowSub?.remove();
+ this.didShowSub?.remove();
+ this.willHideSub?.remove();
+ this.didHideSub?.remove();
};
this.renderTypingIndicator = () => {
if (Platform.OS === 'web') {
Run command yarn or npm i
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | JeanJacquesGourdin |
Solution 2 | Akshil Shah |