Lazy load toots using IntersectionObserver (#3191)
* refactor(components/status_list): Lazy load using IntersectionObserver * refactor(components/status_list): Avoid setState bottleneck * refactor(components/status_list): Update state correctly * fix(components/status): Render if isIntersecting is undefined * refactor(components/status): Recycle timeout * refactor(components/status): Reduce animation duration * refactor(components/status): Use requestIdleCallback * chore: Split polyfill bundles * refactor(components/status_list): Increase rootMargin to 300% * fix(components/status): Check if onRef is not defined * chore: Add note about polyfill bundle splitting * fix(components/status): Reduce animation duration to 0.3 seconds
This commit is contained in:
parent
676ba50601
commit
8e4d1cba00
8 changed files with 146 additions and 13 deletions
|
@ -32,12 +32,44 @@ class Status extends ImmutablePureComponent {
|
|||
onOpenMedia: PropTypes.func,
|
||||
onOpenVideo: PropTypes.func,
|
||||
onBlock: PropTypes.func,
|
||||
onRef: PropTypes.func,
|
||||
isIntersecting: PropTypes.bool,
|
||||
me: PropTypes.number,
|
||||
boostModal: PropTypes.bool,
|
||||
autoPlayGif: PropTypes.bool,
|
||||
muted: PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
isHidden: false,
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.isIntersecting === false && this.props.isIntersecting !== false) {
|
||||
requestIdleCallback(() => this.setState({ isHidden: true }));
|
||||
} else {
|
||||
this.setState({ isHidden: !nextProps.isIntersecting });
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate (nextProps, nextState) {
|
||||
if (nextProps.isIntersecting === false && this.props.isIntersecting !== false) {
|
||||
return nextState.isHidden;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
handleRef = (node) => {
|
||||
if (this.props.onRef) {
|
||||
this.props.onRef(node);
|
||||
|
||||
if (node && node.children.length !== 0) {
|
||||
this.height = node.clientHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
const { status } = this.props;
|
||||
this.context.router.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
|
||||
|
@ -52,12 +84,22 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
render () {
|
||||
let media = '';
|
||||
let media = null;
|
||||
let statusAvatar;
|
||||
const { status, account, ...other } = this.props;
|
||||
const { status, account, isIntersecting, onRef, ...other } = this.props;
|
||||
const { isHidden } = this.state;
|
||||
|
||||
if (status === null) {
|
||||
return <div />;
|
||||
return <div ref={this.handleRef} data-id={status.get('id')} />;
|
||||
}
|
||||
|
||||
if (isIntersecting === false && isHidden) {
|
||||
return (
|
||||
<div ref={this.handleRef} data-id={status.get('id')} style={{ height: `${this.height}px`, opacity: 0 }}>
|
||||
{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}
|
||||
{status.get('content')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||
|
@ -70,7 +112,7 @@ class Status extends ImmutablePureComponent {
|
|||
const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) };
|
||||
|
||||
return (
|
||||
<div className='status__wrapper'>
|
||||
<div className='status__wrapper' ref={this.handleRef} data-id={status.get('id')} >
|
||||
<div className='status__prepend'>
|
||||
<div className='status__prepend-icon-wrapper'><i className='fa fa-fw fa-retweet status__prepend-icon' /></div>
|
||||
<FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name muted'><strong dangerouslySetInnerHTML={displayNameHTML} /></a> }} />
|
||||
|
@ -98,7 +140,7 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={`status ${this.props.muted ? 'muted' : ''} status-${status.get('visibility')}`}>
|
||||
<div className={`status ${this.props.muted ? 'muted' : ''} status-${status.get('visibility')}`} data-id={status.get('id')} ref={this.handleRef}>
|
||||
<div className='status__info'>
|
||||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue