react-stay-scrolled v9.0.0
react-stay-scrolled
Keep your component, such as message boxes, scrolled down
Live demo
You can see the simplest demo here: Live demo
Install
$ npm install --save react-stay-scrolled
Examples
Run examples:
$ npm ci
$ cd examples
$ npm ci
$ npm start
Usage
import { useRef, useLayoutEffect } from 'react';
import PropTypes from 'prop-types';
import useStayScrolled from 'react-stay-scrolled';
const Messages = ({ messages }) => {
const listRef = useRef();
const { stayScrolled/*, scrollBottom*/ } = useStayScrolled(listRef);
// Typically you will want to use stayScrolled or scrollBottom inside
// useLayoutEffect, because it measures and changes DOM attributes (scrollTop) directly
useLayoutEffect(() => {
stayScrolled();
}, [messages.length])
return (
<ul ref={listRef}>
{messages.map((message, i) => <Message key={i} text={message} />)}
</ul>
);
};
Messages.propTypes = {
messages = PropTypes.array,
}
Another use case is notifying users when there is a new message down the window that they haven't read:
// messages.jsx
import { useState, useRef, useCallback, useLayoutEffect } from 'react';
import PropTypes from 'prop-types';
import useStayScrolled from 'react-stay-scrolled';
import Message from './message.jsx';
const Messages = ({ messages }) => {
const [notifyNewMessage, setNotifyNewMessage] = useState(false);
const ref = useRef();
const { stayScrolled, isScrolled } = useStayScrolled(ref);
// The element just scrolled down - remove new messages notification, if any
const onScroll = useCallback(() => {
if (isScrolled()) setNotifyNewMessage(false);
}, []);
useLayoutEffect(() => {
// Tell the user to scroll down to see the newest messages if the element wasn't scrolled down
setNotifyNewMessage(!stayScrolled());
}, [messages.length])
return (
<div ref={ref} onScroll={onScroll}>
{messages.map((message, i) => <Message key={i} text={message} />)}
{notifyNewMessage && <div>Scroll down to new message</div>}
</div>
);
};
You can use react-spring to animate the scroll:
import { useRef, useCallback, useLayoutEffect } from 'react';
import useStayScrolled from 'react-stay-scrolled';
import { useSpring, animated } from '@react-spring/web';
const SpringStayScrolled = ({
provideControllers,
onScroll,
getRunScroll,
}) => {
const ref = useRef(null);
const [{ scrollTop }, animateScroll] = useSpring(() => ({ scrollTop: 0 }), []);
const runScroll = useCallback(offset => animateScroll.start({
scrollTop: offset,
from: { scrollTop: ref.current ? ref.current.scrollTop : 0 },
}), [animateScroll])
const { scrollBottom } = useStayScrolled(ref, { runScroll });
useLayoutEffect(() => { scrollBottom(); }, []);
return (
<animated.div ref={ref} scrollTop={scrollTop}>
{/* ... */}
</animated.div>
);
};
Arguments
ref
Type: a React ref
, required
A ref
to the DOM element whose scroll position you want to control
options
Type: object
, default:
{
initialScroll: null,
inaccuracy: 0,
runScroll: defaultRunScroll,
}
options.initialScroll
Type: number
, default: null
If provided, the scrolling element will mount scrolled with the provided value. If Infinity
is provided, it will mount scrolled to the bottom.
options.inaccuracy
Type: number
, default: 0
Defines an error margin, in pixels, under which stayScrolled
will still scroll to the bottom
options.runScroll
Type: function: (offset) => undefined
, default: (offset) => { ref.current.scrollTop = offset; }
where ref
is the first value
Used for animating dom scrolling. You can use dynamic.js, Velocity, jQuery, or your favorite animation library. Here are examples of possible, tested runScroll
values:
const easing = 'linear';
const duration = 100;
const dynamicsRunScroll = (domRef) => (offset) => {
dynamics.animate(domRef.current, {
scrollTop: offset,
}, {
type: dynamics[easing],
duration,
});
};
const jqueryRunScroll = (domRef) => (offset) => {
jQuery(domRef.current).animate({ scrollTop: offset }, duration, easing);
};
const velocityRunScroll = (domRef) => (offset) => {
Velocity(
domRef.current.firstChild,
'scroll',
{
container: domRef.current,
easing,
duration,
offset,
}
);
};
Return value
Type: object
, shape: { stayScrolled, scrollBottom, scroll, isScrolled }
Four functions used for controlling scroll behavior.
stayScrolled
Type: function: () => bool
Scrolls down the element if it was already scrolled down - useful for when a user is reading previous messages, and you don't want to interrupt. Returns true if it performed a scrolled down, false otherwise.
scroll
Type: function: (position: Integer) => void
Scrolls down to the desired position. If given Infinity
, it scrolls to the bottom
scrollDown
Type: function: () => void
Scrolls down the wrapper element, regardless of current position. Equivalent to () => scroll(Infinity)
.
isScrolled
Type: function: () => bool
Returns true if the dom element is scrolled all the way down (within the inaccuracy provided).
License
See the LICENSE file for license rights and limitations (MIT).
1 year ago
3 years ago
3 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago