Skip to content

Commit d9ee369

Browse files
committed
feat: Adding WithLazy and WithClientSideOnly (#92)
1 parent 59eb8eb commit d9ee369

File tree

4 files changed

+77
-1
lines changed

4 files changed

+77
-1
lines changed

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ This is a set of components that is to be used to build a Quintype Node App. Thi
2828
* [Search box](#search-box)
2929
* [SocialShare](#socialshare)
3030
* [StoryElement](#storyelement)
31+
* [WithClientSideOnly](#withclientsideonly)
3132
* [WithError](#witherror)
3233
* [WithHostUrl](#withhosturl)
34+
* [WithLazy](#withlazy)
3335
* [WithMember](#withmember)
3436
* [WithPreview](#withpreview)
3537
* [WithSocialLogin](#withsociallogin)
@@ -102,7 +104,7 @@ function interstitial(index) {
102104
```
103105

104106
### ClientSideOnly
105-
This component will be loaded by client, and bypassed when doing server side rendering.
107+
This component will be loaded by client, and bypassed when doing server side rendering. Also see [WithClientSideOnly](#WithClientSideOnly) for a render props version.
106108

107109
```javascript
108110
import { ClientSideOnly } from '@quintype/components';
@@ -520,6 +522,19 @@ For different quality images in Image Story Element, pass `imageWidths` and `ima
520522
```
521523
<StoryElement story={story} element={element} imageWidths={[420,1040,1600]} imageDefaultWidth={1040}/>
522524
```
525+
526+
### WithClientSideOnly
527+
This component calls the render prop with true if the client side is completely loaded, and false during SSR and initial bootup.
528+
529+
```javascript
530+
import { WithClientSideOnly } from '@quintype/components';
531+
<WithClientSideOnly>
532+
{({clientSideRendered}) => (
533+
{clientSideRendered && <span>This will be shown only on the client side</span>}
534+
)}
535+
</WithClientSideOnly>
536+
```
537+
523538
### WithError
524539
This function can be used to generate a wrapper component that implements `componentDidCatch()`.
525540

@@ -548,6 +563,18 @@ import { WithHostUrl } from '@quintype/components';
548563
}</WithHostUrl>
549564
```
550565

566+
### WithLazy
567+
568+
This component can be used to load some DOM just before it scrolls into the screen. Currently, it does not support unloading. The `margin` prop is passed to `IntersectionObserver`.
569+
570+
```javascript
571+
import { WithLazy } from '@quintype/components';
572+
573+
<WithLazy margin="50px">{() =>
574+
<SomeHeavyComponent />
575+
}</WithLazy>
576+
```
577+
551578
### WithMember
552579
This is a render props component which will call your callback with the current logged in member. It will automatically call `/api/v1/members/me` to figure out if you are logged in, and replace the contents in the store and callback. In future, this may use LocalStorage to cache the member for some time.
553580

src/components/client-side-only.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ class ClientSideOnlyBase extends React.Component {
1414
}
1515
}
1616

17+
const defaultFallback = () => <span />
18+
19+
function WithClientSideOnlyBase({clientSideRendered = false, children}) {
20+
return children({clientSideRendered})
21+
}
22+
1723
function mapStateToProps(state) {
1824
return {
1925
clientSideRendered: state.clientSideRendered
@@ -25,3 +31,4 @@ function mapDispatchToProps(dispatch) {
2531
}
2632

2733
export const ClientSideOnly = connect(mapStateToProps, mapDispatchToProps)(ClientSideOnlyBase);
34+
export const WithClientSideOnly = connect(mapStateToProps, mapDispatchToProps)(WithClientSideOnlyBase);

src/components/with-lazy.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export class WithLazy extends React.Component {
2+
constructor(props) {
3+
super(props);
4+
this.state = { loaded: false };
5+
this.observerRef = React.createRef();
6+
}
7+
8+
render() {
9+
if (this.state.loaded) {
10+
return this.props.children();
11+
} else {
12+
return <div style={{ height: this.props.height || 50 }} ref={this.observerRef} />;
13+
}
14+
}
15+
16+
componentDidMount() {
17+
this.observer = new global.IntersectionObserver((entries, observer) => this.observerCallback(entries, observer), {
18+
rootMargin: this.props.margin || "160px"
19+
});
20+
this.observer.observe(this.observerRef.current);
21+
}
22+
23+
componentWillUnmount() {
24+
this.observer.disconnect();
25+
}
26+
27+
observerCallback(entries, observer) {
28+
entries.forEach(entry => {
29+
if (entry.isIntersecting || entry.isIntersecting === undefined) {
30+
this.setState({ loaded: true });
31+
observer.disconnect();
32+
}
33+
});
34+
}
35+
}
36+
37+
WithLazy.propTypes = {
38+
children: PropTypes.func.isRequired,
39+
margin: PropTypes.string,
40+
height: PropTypes.number
41+
};

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export * from './components/update-on-interval';
2727
export * from './components/access-type';
2828
export * from './components/adbutler-ad';
2929
export * from './components/with-host-url';
30+
export * from './components/with-lazy';
3031

3132
export * from './store/actions';
3233
export * from './store/reducers';

0 commit comments

Comments
 (0)