Package Exports
- react-cool-inview
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (react-cool-inview) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
π§ This package is in-progress, API may be changed frequently. I don't recommend you to use it now. If you're willing to be an early user. Please note any changes via release. Here's the milestone.
React Cool Inview
A React hook that monitors an element enters or leaves the viewport (or another element) with performant and efficient way, using Intersection Observer. It's lightweight and super flexible, which can help you do many things, like lazy-loading images and videos, infinite scrolling web app, triggering animations, tracking impressions etc. Try it you will ππ» it!
Milestone
- Detect an element is in-view or not
-
onEnter
,onLeave
,onChange
events - Trigger once feature
- Server-side rendering compatibility
- Support Intersection Observer v2
- Unit testing
- Demo app
- Demo code
- Typescript type definition
- Documentation
Features
Coming soon...
Requirement
To use react-cool-inview
, you must use react@16.8.0
or greater which includes hooks.
Installation
This package is distributed via npm.
$ yarn add react-cool-inview
# or
$ npm install --save react-cool-inview
Usage
react-cool-inview
has a flexible API design, it can cover simple to complex use cases for you. Here are some ideas for how you can use it.
β οΈ Most modern browsers support Intersection Observer natively. You can also add polyfill for full browser support.
Basic Use Case
To monitor an element enters or leaves the browser viewport by the inView
state and useful sugar events.
import React, { useRef } from 'react';
import useInView from 'react-cool-inview';
const App = () => {
const ref = useRef();
const { inView, entry } = useInView(ref, {
threshold: 0.25, // Default is 0
onEnter: ({ entry, observe, unobserve }) => {
// Triggered when the target enters the browser viewport (start intersecting)
},
onLeave: ({ entry, observe, unobserve }) => {
// Triggered when the target leaves the browser viewport (end intersecting)
},
// More useful options...
});
return <div ref={ref}>{inView ? 'Hello, I am π€' : 'Bye, I am π΄'}</div>;
};
Lazy-loading Images
It's super easy to build an image lazy-loading component with react-cool-inview
to boost the performance of your web app.
import React, { useRef } from 'react';
import useInView from 'react-cool-inview';
const LazyImage = ({ width, height, ...rest }) => {
const ref = useRef();
const { inView } = useInView(ref, {
// Stop observe when meet the threshold, so the "inView" only triggered once
unobserveOnEnter: true,
// Grow the root margin, so the image will be started loading before it comes to the viewport (better UX)
rootMargin: '50px',
});
return (
<div className="placeholder" style={{ width, height }} ref={ref}>
{inView && <img {...rest} />}
</div>
);
};
π‘ Looking for a comprehensive image component? Try react-cool-img, it's my other component library.
Infinite Scrolling
Infinite scrolling is a popular design technique like Facebook and Twitter feed etc., new content being loaded as you scroll down a page. The basic concept as below.
import React, { useRef, useState } from 'react';
import useInView from 'react-cool-inview';
import axios from 'axios';
const App = () => {
const [todos, setTodos] = useState(['todo-1', 'todo-2', '...']);
const ref = useRef();
useInView(ref, {
// Grow the root margin, so the data will be started loading before user see the loading indicator (better UX)
rootMargin: '50px 0',
// When the loading indicator comes to the viewport
onEnter: ({ unobserve, observe }) => {
// Pause observe when loading data
unobserve();
// Load more data
axios.get('/todos').then((res) => {
setTodos([...todos, ...res.todos]);
// Resume observe after loading data
observe();
});
},
});
return (
<div>
{todos.map((todo) => (
<div>{todo}</div>
))}
<div ref={ref}>Loading...</div>
</div>
);
};
Compare to pagination, infinite scrolling provides a seamless experience for users and itβs easy to see the appeal. But when it comes to render a large lists, performance will be a problem. We can use react-window to address the problem by the technique of DOM recycling.
import React, { useRef, useState } from 'react';
import useInView from 'react-cool-inview';
import { FixedSizeList as List } from 'react-window';
import axios from 'axios';
const Row = ({ index, data, style }) => {
const isLast = index === todos.length;
const { todos, setTodos, isFetching, setIsFetching } = data;
const ref = useRef();
useInView(ref, {
rootMargin: '50px 0',
onEnter: () => {
// Row component is dynamically created by react-window, so use the "isFetching" to avoid re-fetching data
if (!isFetching)
axios.get('/todos').then((res) => {
setTodos((prevTodos) => [...prevTodos, ...res.todos]);
setIsFetching(false);
});
setIsFetching(true);
},
});
return (
<div style={style} ref={isLast ? ref : null}>
{isLast ? 'Loading...' : todos[index]}
</div>
);
};
const App = () => {
const [todos, setTodos] = useState(['todo-1', 'todo-2', '...']);
const [isFetching, setIsFetching] = useState(false);
// Leverage the power of react-window to help us address the performance bottleneck
return (
<List
height={150}
itemCount={todos.length + 1} // Last one is for the loading indicator
itemSize={35}
width={300}
itemData={{ todos, setTodos, isFetching, setIsFetching }}
>
{Row}
</List>
);
};
Trigger Animations
Another great use case is to trigger CSS animations once they are visible to the users.
import React, { useRef } from 'react';
import useInView from 'react-cool-inview';
const App = () => {
const ref = useRef();
const { inView } = useInView(ref, {
// Stop observe when meet the threshold, so the "inView" only triggered once
unobserveOnEnter: true,
// Shrink the root margin, so the animation will be triggered once an element reach a fixed amount of visible
rootMargin: '-100px 0',
});
return (
<div className="container" ref={ref}>
<div className={inView ? 'fade-in' : ''}>I'm a π</div>
</div>
);
};
Track Impressions
Coming soon...
API
Coming soon...
Intersection Observer Polyfill
Intersection Observer has good support amongst browsers, but it's not universal. You'll need to polyfill browsers that don't support it. Polyfills is something you should do consciously at the application level. Therefore react-cool-inview
doesn't include it.
You can use W3C's polyfill:
$ yarn add intersection-observer
# or
$ npm install --save intersection-observer
Then import it at your app's entry point:
import 'intersection-observer';
Or load the polyfill only if needed:
if (!window.IntersectionObserver) require('intersection-observer');
Polyfill.io is an alternative way to add the polyfill when needed.
Contributors β¨
Thanks goes to these wonderful people (emoji key):
Welly π» π π§ |
This project follows the all-contributors specification. Contributions of any kind welcome!