如何解决无法在已卸载的组件上执行React状态更新 =>每当我更改页面时,React Native的useEffect循环都会调用更多次吗?
堆栈为:
- TypeScript
- 反应原生(无人参与)
- 反应本地Firebase程序包
- Firebase
- Firestore
我有一个应用程序,其中创建了2个钩子和一个用于上/下投票的组件。
第一个自定义钩子是通用的Firebase文档处理程序。 每次我更改页面或重新加载页面时,都会越来越多地调用此循环。
我有这个警告:
无法在已卸载的组件上执行React状态更新。这是无操作,但表示您的应用程序中发生内存泄漏。要修复,请取消使用useEffect清理功能中的所有订阅和异步任务。
const useDocument = (ref: DocumentReference,create = false): UseDocumentType => {
const [documentData,setDocumentData] = useState<DocumentData>({})
const [documentExists,setDocumentExists] = useState<'untested' | boolean>('untested')
const [updating,setUpdating] = useState(false)
// Listen to the user post data
useEffect(() => {
// Declare the function that will be used to subscribe
const subscriber = (): void => {
try {
ref?.onSnapshot((documentSnapshot: DocumentSnapshot) => {
if (documentSnapshot && documentSnapshot.exists) {
setUpdating(false)
setDocumentExists(true)
const data = documentSnapshot.data() || {}
data.id = documentSnapshot.id
setDocumentData(data)
// The screen is covered by the above console.log,while not infinite
console.log(`DEBUG > useDocument > subscriber > document ${ref.path} data fetched`)
} else if (create) {
// Create the document with a single field
updateDocument({ createdOn: new Date() })
console.log(`DEBUG > useDocument > subscriber > document ${ref.path} created by subscriber`)
} else {
setDocumentExists(false)
console.log(`WARN > useDocument > subscriber > the document ${ref.path || '<unknown ref>'} does not exists in Firebase`)
}
})
} catch (error) {
throw functionError(error,'subscriber','','useDocument')
}
}
subscriber()
// Stop listening for updates when no longer required
return (): void => subscriber()
// Disabling ESLint rule since passing refs in hooks dependencies create infinite rerenders
// Refs are functions and shall be wrapped into useCallback to avoid that.
// eslint-disable-next-line react-hooks/exhaustive-deps
},[create,shallExist])
// Function used to update documents
const updateDocument = async (data: Partial<DocumentData>): Promise<void> => {
try {
setUpdating(true)
await ref?.set(data,{ merge: true })
// This function can also create the document
setDocumentExists(true)
} catch (error) {
throw functionError(error,'updateDocument',data,'useDocument')
}
}
return { documentExists,documentData,updating,setUpdating,updateDocument }
}
然后由useFeedback自定义钩子调用:
const useFeedback = (user: AuthUser,postId: string): UseFeedbackType => {
// State
const [upvoted,setUpvoted] = useState<boolean>(false)
const [downvoted,setDownvoted] = useState<boolean>(false)
// Reference to the document handling user feedback for this post
// Shall it be passed into a useCallback hook?
const ref: DocumentReference<DocumentData> = userPostDoc(user,postId)
// useDocument provides tooling necessary to work with the document
const { documentExists,updateDocument,toggleDocumentField,withTransaction } = useDocument(ref)
// Update the state when the document data change
useEffect(() => {
console.log(`DEBUG > useFeedback > useEffect > liked=${documentData?.upvoted},muted=${documentData?.downvoted}`)
if (user) {
setUpvoted(documentData?.upvoted || false)
setMuted(documentData?.downvoted|| false)
}
},[documentData,user])
const onUpvote = async (): Promise<void> => {
if (upvoted) {
await updateDocument({ upvoted: false })
setUpvoted(false)
} else {
await updateDocument({ upvoted: true})
setUpvoted(true)
}
}
const onMute = async (): Promise<void> => {
if (muted) {
await updateDocument({ downvoted: false })
setMuted(false)
} else {
await updateDocument({ downvoted: true })
setMuted(true)
}
}
return { onUpvote,onMute,upvoted,muted,updating }
}
然后将该组件调用到屏幕中:
const PostScreen: FC<Props> = ({ route,navigation }: Props) => {
const initialPostData = route?.params?.post
//imported: const postDoc = (postId: string) => postsCollection.doc(postId)
const ref = postDoc(initialPostData.id)
const [postData,setPostData] = useState(initialPostData)
const { documentData,documentExists } = useDocument(ref)
// Force re-render
useEffect(() => {
if (documentExists === true) setPostData(documentData)
// This console.log is also printed several time but "only" ~10 times
console.log(
`DEBUG > PostScreen > useEffect > documentExists=${documentExists} and postData=${objectToString(postData || initialPostData)}`,)
},documentExists])
return (
<SafeAreaView style={styles.container}>
<StatusBar backgroundColor="transparent" barStyle="dark-content" translucent={true} />
<ScrollView>
<FavoriteIcon data={postData} style={styles.bookmark} color={colors.white} size={50} />
<View style={styles.contentContainer}>
<Text style={styles.title}>{postData?.title}</Text>
<Text style={styles.description}>{postData?.description || ''}</Text>
<FeedbackIcons postId={postData?.id} />
</View>
</ScrollView>
</SafeAreaView>
)
}
我是React的新手。这实际上可行,但更新时间很长(尽管Firebase足够聪明,可以在上/下投票时使用它的缓存,但设置起来需要2秒),并且useEffect的日志记录很长,使我认为存在严重的优化问题...
感谢您的耐心和帮助!
编辑 :
重新启动Android模拟器后,每次我上/下一篇文章时,只有1个调用useFeedback和3个useDocument。
保存了几次代码后,我现在有10个对useFeedback的调用,每个之间有1到4个对useDocument的调用...并且每次我保存代码或更改页面时,它都会变得越来越糟。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。