我有一个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>
);
};
答案 0 :(得分:0)
我意识到问题更多是css而不是react代码。我没有为容器设置明确的高度,并且我认为正是这种不确定性导致浏览器在列表元素自己重新渲染/更新时突然在容器中上下滚动。