Javascript 反应由交叉点观察者引起的内存泄漏
背景信息:我的React应用程序在手机上崩溃。Chrome开发工具表明没有触发垃圾回收。查看堆,按保留大小排列的顶级构造函数都引用了intersection observor(在我的许多react组件中用于无限滚动) 问题:我如何着手修复由交叉点观察者造成的内存泄漏?有没有一种方法可以在组件卸载时触发垃圾收集 使用交叉点观测器进行无限滚动的示例组件:Javascript 反应由交叉点观察者引起的内存泄漏,javascript,reactjs,memory-leaks,garbage-collection,google-chrome-devtools,Javascript,Reactjs,Memory Leaks,Garbage Collection,Google Chrome Devtools,背景信息:我的React应用程序在手机上崩溃。Chrome开发工具表明没有触发垃圾回收。查看堆,按保留大小排列的顶级构造函数都引用了intersection observor(在我的许多react组件中用于无限滚动) 问题:我如何着手修复由交叉点观察者造成的内存泄漏?有没有一种方法可以在组件卸载时触发垃圾收集 使用交叉点观测器进行无限滚动的示例组件: import React, { useEffect, useState, useRef, useCallback } from 'react' i
import React, { useEffect, useState, useRef, useCallback } from 'react'
import { Link } from 'react-router-dom'
import { uuid, fabricateTimeStamp, getRandom } from '../containers/helperFunctions'
import { avatarQuery } from '../words'
import quote from 'inspirational-quotes'
import history from '../history'
import { thumbsUp, thumbsDown, arrowDrop } from './svgs'
import {
fetchAvatars as callAvatarsAPI,
fetchVideos as callVideosAPI } from '../containers/api'
const ActivityFeed = (props) => {
const [firstRenderDone, setFirstRenderDone] = useState()
const [comments, setComments] = useState([])
useEffect(() => {
const mobile = window.innerWidth <= 600
if (mobile) fetchAvatars('woman', 3)
if (!mobile) fetchAvatars('woman', 6)
if (history.location.pathname.includes('/video/') || history.location.pathname.includes('/search/')) {
document.querySelector('.activityFeedContainer').classList.toggle('hide')
}
}, [])
// if user clicks nav button, remove all comments generated by infinite scroll
useEffect(() => {
setComments(prevState => (prevState.slice(0, 8)))
}, [props.button])
// INFINITE SCROLL
// Callback is triggered when ref is set in mapCommentsToHTML
const observer = useRef()
const lastActivityPost = useCallback(lastPostNode => {
observer.current = new IntersectionObserver(entries => {
const lastPost = entries[0]
if (lastPost.isIntersecting) fetchAvatars(getRandom(avatarQuery), 6)
})
if (lastPostNode) observer.current.observe(lastPostNode)
})
const fetchAvatars = async (query, amount) => {
let response = await callAvatarsAPI(query, amount)
response = response.data.hits
mapCommentsToHTML(response)
}
const mapCommentsToHTML = (response) => {
const picsMappedToHTML = response.map((pic, index) => {
return (
<div className="commentWrapper" key={uuid()} ref={response.length === index + 1 ? lastActivityPost : null}>
<div className="avatarPlaceholder--comments">
{props.page === 'channel'
? <img
className="avatarPlaceholder--img"
src={
props.userAvatar.previewURL ? props.userAvatar.previewURL
: props.userAvatar.userImageURL === "" ? 'https://i.imgur.com/ZwDgXSF.jpg'
: props.userAvatar.userImageURL}
alt="An Activity Feed User Avatar" />
: <Link to={`/channel/${pic.id}`}>
<img
className="avatarPlaceholder--img"
src={pic.previewURL}
alt="An Activity Feed User Avatar" />
</Link>
}
</div>
<div className="commentContainer" >
<Link to={`/channel/${pic.id}`}>
<h5 className="commentorName">{props.page === 'channel' ? props.userName : pic.user}</h5>
</Link>
<span className="dateOfComment">{fabricateTimeStamp(index)}</span>
<p className={`${props.page}-comment`}>{quote.getQuote().text}</p>
<div className="thumbs">
<span className="thumbsUpIcon">
{thumbsUp(16)}
</span>
<span className="thumbsDownIcon">
{thumbsDown(16)}
</span>
</div>
<p className="replyText">REPLY</p>
<div className="viewReplies">
<span className="arrowDrop">
{arrowDrop()}
</span>
View {Math.floor(Math.random() * 50) + 2} Replies
</div>
</div>
</div>
)
})
setComments(prevState => ([...prevState, ...picsMappedToHTML]))
}
return (
<aside className="activityFeedContainer">
<h1 className={`${props.page}--activity-feed-title`}>Activity Feed</h1>
<hr className="home--activityfeed-hr" />
<div className="commentSection--activityfeed">
{comments}
</div>
</aside>
)
}
export default ActivityFeed
import React,{useffect,useState,useRef,useCallback}from'React'
从“react router dom”导入{Link}
从“../containers/helperFunctions”导入{uuid,factureTimeStamp,getRandom}
从“../words”导入{avatarQuery}
从“励志格言”导入格言
从“../history”导入历史记录
从“/svgs”导入{thumbsUp、thumbsDown、arrowDrop}
导入{
获取头像作为callAvatarsAPI,
从“../containers/api”以callVideosAPI}的形式获取视频
const ActivityFeed=(道具)=>{
const[firstRenderDone,setFirstRenderDone]=useState()
const[comments,setComments]=useState([])
useffect(()=>{
const mobile=window.innerWidth{
setComments(prevState=>(prevState.slice(0,8)))
},[props.按钮])
//无限卷轴
//在mapCommentsToHTML中设置ref时触发回调
const observer=useRef()
const lastActivityPost=useCallback(lastPostNode=>{
observer.current=新的IntersectionObserver(条目=>{
const lastPost=条目[0]
如果(lastPost.isIntersecting)获取头像(getRandom(avatarQuery),6)
})
if(lastPostNode)observer.current.observe(lastPostNode)
})
const fetchAvatars=async(查询,金额)=>{
let response=wait callAvatarsAPI(查询,金额)
response=response.data.hits
mapCommentsToHTML(响应)
}
常量mapCommentsToHTML=(响应)=>{
const picsMappedToHTML=response.map((pic,index)=>{
报税表(
{props.page==='channel'
?
:
}
{props.page==='channel'?props.userName:pic.user}
{factureTimeStamp(索引)}
{quote.getQuote().text}
{thumbsUp(16)}
{thumbsDown(16)}
回复
{arrowDrop()}
查看{Math.floor(Math.random()*50)+2}回复
)
})
setComments(prevState=>([…prevState,…picsMappedToHTML]))
}
返回(
堆快照:
import React, { useEffect, useState, useRef, useCallback } from 'react'
import { Link } from 'react-router-dom'
import { uuid, fabricateTimeStamp, getRandom } from '../containers/helperFunctions'
import { avatarQuery } from '../words'
import quote from 'inspirational-quotes'
import history from '../history'
import { thumbsUp, thumbsDown, arrowDrop } from './svgs'
import {
fetchAvatars as callAvatarsAPI,
fetchVideos as callVideosAPI } from '../containers/api'
const ActivityFeed = (props) => {
const [firstRenderDone, setFirstRenderDone] = useState()
const [comments, setComments] = useState([])
useEffect(() => {
const mobile = window.innerWidth <= 600
if (mobile) fetchAvatars('woman', 3)
if (!mobile) fetchAvatars('woman', 6)
if (history.location.pathname.includes('/video/') || history.location.pathname.includes('/search/')) {
document.querySelector('.activityFeedContainer').classList.toggle('hide')
}
}, [])
// if user clicks nav button, remove all comments generated by infinite scroll
useEffect(() => {
setComments(prevState => (prevState.slice(0, 8)))
}, [props.button])
// INFINITE SCROLL
// Callback is triggered when ref is set in mapCommentsToHTML
const observer = useRef()
const lastActivityPost = useCallback(lastPostNode => {
observer.current = new IntersectionObserver(entries => {
const lastPost = entries[0]
if (lastPost.isIntersecting) fetchAvatars(getRandom(avatarQuery), 6)
})
if (lastPostNode) observer.current.observe(lastPostNode)
})
const fetchAvatars = async (query, amount) => {
let response = await callAvatarsAPI(query, amount)
response = response.data.hits
mapCommentsToHTML(response)
}
const mapCommentsToHTML = (response) => {
const picsMappedToHTML = response.map((pic, index) => {
return (
<div className="commentWrapper" key={uuid()} ref={response.length === index + 1 ? lastActivityPost : null}>
<div className="avatarPlaceholder--comments">
{props.page === 'channel'
? <img
className="avatarPlaceholder--img"
src={
props.userAvatar.previewURL ? props.userAvatar.previewURL
: props.userAvatar.userImageURL === "" ? 'https://i.imgur.com/ZwDgXSF.jpg'
: props.userAvatar.userImageURL}
alt="An Activity Feed User Avatar" />
: <Link to={`/channel/${pic.id}`}>
<img
className="avatarPlaceholder--img"
src={pic.previewURL}
alt="An Activity Feed User Avatar" />
</Link>
}
</div>
<div className="commentContainer" >
<Link to={`/channel/${pic.id}`}>
<h5 className="commentorName">{props.page === 'channel' ? props.userName : pic.user}</h5>
</Link>
<span className="dateOfComment">{fabricateTimeStamp(index)}</span>
<p className={`${props.page}-comment`}>{quote.getQuote().text}</p>
<div className="thumbs">
<span className="thumbsUpIcon">
{thumbsUp(16)}
</span>
<span className="thumbsDownIcon">
{thumbsDown(16)}
</span>
</div>
<p className="replyText">REPLY</p>
<div className="viewReplies">
<span className="arrowDrop">
{arrowDrop()}
</span>
View {Math.floor(Math.random() * 50) + 2} Replies
</div>
</div>
</div>
)
})
setComments(prevState => ([...prevState, ...picsMappedToHTML]))
}
return (
<aside className="activityFeedContainer">
<h1 className={`${props.page}--activity-feed-title`}>Activity Feed</h1>
<hr className="home--activityfeed-hr" />
<div className="commentSection--activityfeed">
{comments}
</div>
</aside>
)
}
export default ActivityFeed
我认为你需要在某个时候停止观察,你可能想
卸载时调用IntersectionObserver我认为您需要在某个时候停止观察,您可能希望
理想情况下,当您卸载时调用IntersectionObserver,我会将IntersectionObserver放在useEffect钩子中,当组件卸载时,我会调用Observer.unobserve()理想情况下,我会将IntersectionObserver放在useEffect钩子中,当组件卸载时,我会调用Observer.unobserve()