Skip to content

Instantly share code, notes, and snippets.

@hypervillain
Last active October 16, 2018 17:38
Show Gist options
  • Save hypervillain/3db4e68a5d6d6dbdaf454d22c671f01f to your computer and use it in GitHub Desktop.
Save hypervillain/3db4e68a5d6d6dbdaf454d22c671f01f to your computer and use it in GitHub Desktop.
Generic React Affix component, using react-measure
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Measure from 'react-measure';
import classnames from 'classnames';
// replace this if you don't use CSS modules
import cls from './affix.module.scss';
class Affix extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
FixedComponent: PropTypes.node || PropTypes.string,
offset: PropTypes.number,
}
static defaultProps = {
offset: document.documentElement.scrollTop || document.body.scrollTop || 0,
FixedComponent: 'div',
};
state = {
affix: false,
};
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
const { affix } = this.state;
const { offset } = this.props;
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if (!affix && scrollTop >= offset) {
this.setState({
affix: true,
});
}
if (affix && scrollTop < offset) {
this.setState({
affix: false,
});
}
};
render() {
const isAffix = this.state.affix;
const {
className,
FixedComponent,
wrapperStyle,
style,
...rest
} = this.props;
return (
<Measure
bounds
onResize={contentRect =>
this.setState({ parentWidth: contentRect.bounds.width })
}
>
{({ measureRef }) => (
<div className={cls.wrapper} ref={measureRef} style={wrapperStyle}>
<FixedComponent
{...rest}
className={classnames(isAffix ? cls.affix : null, className)}
style={{ width: this.state.parentWidth, ...style }}
>
{this.props.children}
</FixedComponent>
</div>
)}
</Measure>
);
}
}
Affix.displayName = 'Affix';
export default Affix;
$pagePadding: 60px;
.wrapper {
width: 100%;
z-index: 1;
}
.affix {
position: fixed;
// if children.offsetHeight may be > 100vh
// allow an overflow + define some bottom padding
overflow: auto;
max-height: 95%;
padding-bottom: $pagePadding;
z-index: 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment