Skip to content

Commit 64f50ac

Browse files
committed
Handle 'onVisible' events in LazyLoadComponent, to avoid re-rendering all components stack
1 parent 646cd41 commit 64f50ac

7 files changed

+303
-305
lines changed
Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,89 @@
11
import React from 'react';
2+
import { PropTypes } from 'prop-types';
23

3-
import LazyLoadComponentWithoutTracking
4-
from './LazyLoadComponentWithoutTracking.jsx';
5-
import LazyLoadComponentWithTracking
6-
from './LazyLoadComponentWithTracking.jsx';
4+
import PlaceholderWithoutTracking from './PlaceholderWithoutTracking.jsx';
5+
import PlaceholderWithTracking from './PlaceholderWithTracking.jsx';
76

87
class LazyLoadComponent extends React.Component {
98
constructor(props) {
109
super(props);
1110

12-
const { scrollPosition } = props;
11+
const { afterLoad, beforeLoad, scrollPosition, visibleByDefault } = props;
12+
13+
this.state = {
14+
visible: visibleByDefault
15+
};
16+
17+
if (visibleByDefault) {
18+
beforeLoad();
19+
afterLoad();
20+
}
21+
22+
this.onVisible = this.onVisible.bind(this);
1323

1424
this.isScrollTracked = (scrollPosition &&
1525
Number.isFinite(scrollPosition.x) && scrollPosition.x >= 0 &&
1626
Number.isFinite(scrollPosition.y) && scrollPosition.y >= 0);
1727
}
1828

29+
componentDidUpdate(prevProps, prevState) {
30+
if (prevState.visible !== this.state.visible) {
31+
this.props.afterLoad();
32+
}
33+
}
34+
35+
onVisible() {
36+
this.props.beforeLoad();
37+
this.setState({
38+
visible: true
39+
});
40+
}
41+
1942
render() {
20-
if (this.isScrollTracked) {
21-
return <LazyLoadComponentWithoutTracking {...this.props} />;
43+
if (this.state.visible) {
44+
return this.props.children;
2245
}
2346

24-
const { scrollPosition, ...props } = this.props;
47+
const { className, height, placeholder, scrollPosition, style,
48+
threshold, width } = this.props;
2549

26-
return <LazyLoadComponentWithTracking {...props} />;
50+
if (this.isScrollTracked) {
51+
return (
52+
<PlaceholderWithoutTracking
53+
className={className}
54+
height={height}
55+
onVisible={this.onVisible}
56+
placeholder={placeholder}
57+
scrollPosition={scrollPosition}
58+
style={style}
59+
threshold={threshold}
60+
width={width} />
61+
);
62+
}
63+
64+
return (
65+
<PlaceholderWithTracking
66+
className={className}
67+
height={height}
68+
onVisible={this.onVisible}
69+
placeholder={placeholder}
70+
style={style}
71+
threshold={threshold}
72+
width={width} />
73+
);
2774
}
2875
}
2976

77+
LazyLoadComponent.propTypes = {
78+
afterLoad: PropTypes.func,
79+
beforeLoad: PropTypes.func,
80+
visibleByDefault: PropTypes.bool
81+
};
82+
83+
LazyLoadComponent.defaultProps = {
84+
afterLoad: () => ({}),
85+
beforeLoad: () => ({}),
86+
visibleByDefault: false
87+
};
88+
3089
export default LazyLoadComponent;

src/components/LazyLoadComponent.spec.js

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,106 @@ import { configure, mount } from 'enzyme';
44
import Adapter from 'enzyme-adapter-react-16';
55

66
import LazyLoadComponent from './LazyLoadComponent.jsx';
7-
import LazyLoadComponentWithTracking
8-
from './LazyLoadComponentWithTracking.jsx';
9-
import LazyLoadComponentWithoutTracking
10-
from './LazyLoadComponentWithoutTracking.jsx';
7+
import PlaceholderWithTracking from './PlaceholderWithTracking.jsx';
8+
import PlaceholderWithoutTracking from './PlaceholderWithoutTracking.jsx';
119

1210
configure({ adapter: new Adapter() });
1311

1412
const {
15-
scryRenderedComponentsWithType
13+
scryRenderedComponentsWithType,
14+
scryRenderedDOMComponentsWithTag
1615
} = ReactTestUtils;
1716

1817
describe('LazyLoadComponent', function() {
19-
it('renders a LazyLoadComponentWithTracking when scrollPosition is undefined', function() {
18+
it('renders a PlaceholderWithTracking when scrollPosition is undefined', function() {
19+
const lazyLoadComponent = mount(
20+
<LazyLoadComponent
21+
style={{marginTop: 100000}}>>
22+
<p>Lorem Ipsum</p>
23+
</LazyLoadComponent>
24+
);
25+
26+
const placeholderWithTracking = scryRenderedComponentsWithType(
27+
lazyLoadComponent.instance(), PlaceholderWithTracking);
28+
29+
expect(placeholderWithTracking.length).toEqual(1);
30+
});
31+
32+
it('renders a PlaceholderWithoutTracking when scrollPosition is defined', function() {
33+
const lazyLoadComponent = mount(
34+
<LazyLoadComponent
35+
scrollPosition={{ x: 0, y: 0}}
36+
style={{marginTop: 100000}}>>
37+
<p>Lorem Ipsum</p>
38+
</LazyLoadComponent>
39+
);
40+
41+
const placeholderWithoutTracking = scryRenderedComponentsWithType(
42+
lazyLoadComponent.instance(), PlaceholderWithoutTracking);
43+
44+
expect(placeholderWithoutTracking.length).toEqual(1);
45+
});
46+
47+
it('renders children when visible', function() {
2048
const lazyLoadComponent = mount(
2149
<LazyLoadComponent>
2250
<p>Lorem Ipsum</p>
2351
</LazyLoadComponent>
2452
);
2553

26-
const lazyLoadComponentWithTracking = scryRenderedComponentsWithType(
27-
lazyLoadComponent.instance(), LazyLoadComponentWithTracking);
54+
lazyLoadComponent.instance().onVisible();
55+
56+
const paragraphs = scryRenderedDOMComponentsWithTag(
57+
lazyLoadComponent.instance(), 'p');
58+
59+
expect(paragraphs.length).toEqual(1);
60+
});
61+
62+
it('triggers beforeLoad when onVisible is triggered', function() {
63+
const beforeLoad = jest.fn();
64+
const lazyLoadComponent = mount(
65+
<LazyLoadComponent
66+
beforeLoad={beforeLoad}
67+
style={{marginTop: 100000}}>
68+
<p>Lorem Ipsum</p>
69+
</LazyLoadComponent>
70+
);
71+
72+
lazyLoadComponent.instance().onVisible();
73+
74+
expect(beforeLoad).toHaveBeenCalledTimes(1);
75+
});
76+
77+
it('triggers afterLoad when onVisible is triggered', function() {
78+
const afterLoad = jest.fn();
79+
const lazyLoadComponent = mount(
80+
<LazyLoadComponent
81+
afterLoad={afterLoad}
82+
style={{marginTop: 100000}}>
83+
<p>Lorem Ipsum</p>
84+
</LazyLoadComponent>
85+
);
86+
87+
lazyLoadComponent.instance().onVisible();
2888

29-
expect(lazyLoadComponentWithTracking.length).toEqual(1);
89+
expect(afterLoad).toHaveBeenCalledTimes(1);
3090
});
3191

32-
it('renders a LazyLoadComponentWithoutTracking when scrollPosition is defined', function() {
92+
it('triggers beforeLoad and afterLoad when visibleByDefault is true', function() {
93+
const afterLoad = jest.fn();
94+
const beforeLoad = jest.fn();
3395
const lazyLoadComponent = mount(
3496
<LazyLoadComponent
35-
scrollPosition={{ x: 0, y: 0}}>
97+
afterLoad={afterLoad}
98+
beforeLoad={beforeLoad}
99+
style={{marginTop: 100000}}>
36100
<p>Lorem Ipsum</p>
37101
</LazyLoadComponent>
38102
);
39103

40-
const lazyLoadComponentWithoutTracking = scryRenderedComponentsWithType(
41-
lazyLoadComponent.instance(), LazyLoadComponentWithoutTracking);
104+
lazyLoadComponent.instance().onVisible();
42105

43-
expect(lazyLoadComponentWithoutTracking.length).toEqual(1);
106+
expect(afterLoad).toHaveBeenCalledTimes(1);
107+
expect(beforeLoad).toHaveBeenCalledTimes(1);
44108
});
45109
});

src/components/LazyLoadComponentWithTracking.jsx

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)