Skip to content

Instantly share code, notes, and snippets.

@aandrewww
Last active May 21, 2020 13:07
Show Gist options
  • Save aandrewww/a33d5142fd657baf717f4230ae0ece26 to your computer and use it in GitHub Desktop.
Save aandrewww/a33d5142fd657baf717f4230ae0ece26 to your computer and use it in GitHub Desktop.
import React, {
useRef, useEffect, useCallback, useState,
} from 'react';
import { fromEvent, merge } from 'rxjs';
import PropTypes from 'prop-types';
const styles = {
wrapper: {
position: 'relative',
},
sticky: {
position: 'fixed',
zIndex: 2,
top: 0,
},
};
export const StickyWrapper = ({ children, top }) => {
const [isSticky, setSticky] = useState(false);
const ref = useRef(null);
const handleScroll = useCallback(() => {
// Make sure ref has current attibute and getBoundingClientRect function
// otherwise, it could cause getBoundingClientRect undefined error.
if (ref && ref.current && ref.current.getBoundingClientRect()) {
// Then, we compare the distance of the ref component to the top
// with top value we set. If less than, we set isStick true.
const { top: elTop, height: elHeight } = ref.current.getBoundingClientRect();
if (!ref.current.style.height) {
ref.current.style.height = `${elHeight}px`;
}
setSticky(elTop <= top);
}
}, [top]);
useEffect(() => {
const elementSubscription = merge(
fromEvent(window, 'scroll'),
fromEvent(window, 'resize'),
).subscribe(handleScroll);
return () => {
elementSubscription.unsubscribe();
};
}, [handleScroll]);
return (
<div style={styles.wrapper} ref={ref}>
<div style={isSticky ? styles.sticky : {}}>
{children}
</div>
</div>
);
};
StickyWrapper.propTypes = {
children: PropTypes.node.isRequired,
top: PropTypes.number,
};
StickyWrapper.defaultProps = {
top: 0,
};
@aandrewww
Copy link
Author

<StickyWrapper>
  <div>something</div>
</StickyWrapper>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment