[英]React child updates change parent scroll position
我有一個React容器組件,該組件應該包含一個子計時器組件列表。 這些子組件只是倒數計時器。
為此,我的子組件類使用setInterval
每秒更新一次子組件。 隨着更新的進行,我注意到在向下或向上滾動列表時,恰好在子計時器更新自身時,容器組件會突然以明顯大的方式突然向上或向下跳躍,但是從未調用容器的render方法。
除此之外,如果我只是不滾動或停止滾動,跳轉就永遠不會發生。 如果我滾動然后停止滾動,即使我停止滾動, onscroll
處理程序也會與子計時器更新保持同步觸發。
我以前從未遇到過這種情況。 React子代可以在更新時隱式強制其父容器隨機上下滾動嗎?
這是容器的代碼:
class UserContentDetail extends React.Component {
constructor(props) {
super(props);
this.state ={
page: 1,
perPage: 15,
animes: [],
currTab: props.currTab
};
this.startApiRequests = this.startApiRequests.bind(this);
this.handleResponse = this.handleResponse.bind(this);
this.handleData = this.handleData.bind(this);
this.handleError = this.handleError.bind(this);
}
componentDidMount() {
this.startApiRequests();
}
componentDidUpdate() {
if (this.props.currTab !== this.state.currTab) {
this.startApiRequests();
}
}
startApiRequests() {
let currSeason = anilistApiConstants.SEASON_SUMMER;
let currSeasonYear = 2018;
let resultPromise = null;
switch(this.props.currTab) {
case sideNavConstants.SIDE_NAV_TAB_MY_ANIME:
resultPromise = api.getMyAnimes(this.props.myAnimeIds, this.state.page, this.state.perPage);
break;
case sideNavConstants.SIDE_NAV_TAB_POPULAR_ANIME:
resultPromise = api.getPopularAnimes(this.state.page, this.state.perPage);
break;
case sideNavConstants.SIDE_NAV_TAB_NEW_ANIME:
resultPromise = api.getNewAnimes(currSeason, currSeasonYear, this.state.page, this.state.perPage);
break;
}
resultPromise.then(this.handleResponse)
.then(this.handleData)
.catch(this.handleError);
}
handleResponse(response) {
return response.json().then(function (json) {
return response.ok ? json : Promise.reject(json);
});
}
handleData(data) {
let results = data.data.Page.media;
for (let i = 0; i < results.length; ++i) {
if (results[i].nextAiringEpisode == null) {
results[i].nextAiringEpisode = {empty: true};
}
}
this.setState({
page: 1,
perPage: 15,
animes: results,
currTab: this.props.currTab
});
}
handleError(error) {
alert('Error, check console');
console.error(error);
}
render() {
console.log('rendering list');
return(
<div className={userMasterDetailStyles.detailWrapper}>
<div className={userMasterDetailStyles.detailList}>
{this.state.animes.map(anime => <AnimeCard {...anime} key={anime.id} />)}
</div>
</div>
);
}
}
這是計時器的代碼( AnimeCardTime
),並被卡容器( AnimeCard
)包圍:
class AnimeCardTime extends React.Component {
constructor(props) {
super(props);
this.state = {
timeUntilNextEpisode: props.timeLeft,
animeId: props.id
};
this.countdownTimer = null;
this.getNextEpisodeTimeUntilString = this.getNextEpisodeTimeUntilString.bind(this);
this.startTimer = this.startTimer.bind(this);
this.endTimer = this.endTimer.bind(this);
}
componentDidMount() {
this.startTimer();
}
componentWillUnmount() {
this.endTimer();
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.id !== prevState.animeId) {
return {
timeUntilNextEpisode: nextProps.timeLeft,
animeId: nextProps.id
};
}
return null;
}
startTimer() {
if (this.props.timeLeft != undefined) {
this.countdownTimer = setInterval(() => {
this.setState({
timeUntilNextEpisode: this.state.timeUntilNextEpisode - 60,
});
}, 1000);
}
}
endTimer() {
if (this.countdownTimer != null) {
clearInterval(this.countdownTimer);
}
}
secondsToTimeString(timeSecondsUntil) {
timeSecondsUntil = Number(timeSecondsUntil);
let d = Math.floor(timeSecondsUntil / (3600*24));
let h = Math.floor(timeSecondsUntil % (3600*24) / 3600);
let m = Math.floor(timeSecondsUntil % (3600*24) % 3600 / 60);
let dDisplay = d > 0 ? d + 'd ' : '';
let hDisplay = h > 0 ? h + 'hr ' : '';
let mDisplay = m > 0 ? m + 'm ' : '';
return dDisplay + hDisplay + mDisplay;
}
getNextEpisodeTimeUntilString() {
if (this.props.timeLeft != undefined) {
return 'Ep ' + this.props.nextEpisode + ' - ' + this.secondsToTimeString(this.state.timeUntilNextEpisode);
}
else {
return this.props.season + ' ' + this.props.seasonYear;
}
}
render() {
return(<h6 className={userMasterDetailStyles.cardTime}>{this.getNextEpisodeTimeUntilString()}</h6>);
}
}
const AnimeCard = (props) => {
let secondsToTimeString = (timeSecondsUntil) => {
timeSecondsUntil = Number(timeSecondsUntil);
let d = Math.floor(timeSecondsUntil / (3600*24));
let h = Math.floor(timeSecondsUntil % (3600*24) / 3600);
let m = Math.floor(timeSecondsUntil % (3600*24) % 3600 / 60);
let dDisplay = d > 0 ? d + 'd ' : '';
let hDisplay = h > 0 ? h + 'hr ' : '';
let mDisplay = m > 0 ? m + 'm ' : '';
return dDisplay + hDisplay + mDisplay;
};
let getNextEpisodeTimeUntilString = () => {
if (props.status === anilistApiConstants.STATUS_RELEASING) {
return 'Ep ' + props.nextAiringEpisode.episode + ' - ' + secondsToTimeString(props.nextAiringEpisode.timeUntilAiring);
}
else {
return props.season + ' ' + props.startDate.year;
}
};
return(
/* <h6 className={userMasterDetailStyles.cardTime}>{getNextEpisodeTimeUntilString()}</h6> */
<a className={userMasterDetailStyles.animeCardLinkContainer} href={props.siteUrl}>
<div className={userMasterDetailStyles.animeCardContainer}>
<h6 className={userMasterDetailStyles.cardTitle}>{props.title.romaji}</h6>
<AnimeCardTime timeLeft={props.nextAiringEpisode.timeUntilAiring} nextEpisode={props.nextAiringEpisode.episode} season={props.season} seasonYear={props.startDate.year} id={props.id}/>
<img className={userMasterDetailStyles.cardImage} src={props.coverImage.large}/>
<p className={userMasterDetailStyles.cardDescription}>{props.description.replace(/<(?:.|\n)*?>/gm, '')}</p>
<p className={userMasterDetailStyles.cardGenres}>{props.genres.reduce((prev, curr) => {return prev + ', ' + curr;})}</p>
</div>
</a>
);
};
我意識到問題更多是css,而不是react代碼。 我沒有為容器設置明確的高度,並且我認為正是這種不確定性導致瀏覽器在列表元素自己重新渲染/更新時突然在容器中上下滾動。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.