Skip to content

Instantly share code, notes, and snippets.

@ianmstew
Last active May 21, 2021 13:30
Show Gist options
  • Save ianmstew/c271ae8ba4b0afd9d580d59ae26f1f05 to your computer and use it in GitHub Desktop.
Save ianmstew/c271ae8ba4b0afd9d580d59ae26f1f05 to your computer and use it in GitHub Desktop.
HOC vs Render Prop vs Custom Hook
/* Loader that renders empty unless `props.active` is true */
function Loader(props) {
const { active } = props;
return active && <div>Loading...</div>;
}
/* Loader class component that renders empty until a timeout */
class LoaderWithDelay extends React.Component {
state = { active: false };
timerId;
componentDidMount() {
this.timerId = setTimeout(() => {
this.setState({ active: true });
}, this.props.timeoutMs)
}
componentWillUnmount() {
clearTimeout(this.timerId);
}
render() {
const { active } = this.state;
return active && <div>Loading...</div>;
}
}
/* Loader function component that renders empty until a timeout */
function LoaderWithDelay(props) {
const { timeoutMs } = props;
const [active, setActive] = React.useState(false);
const timerIdRef = React.useRef();
React.useEffect(() => {
timerIdRef.current = setTimeout(() => {
setActive(true);
}, timeoutMs);
return () => {
clearTimeout(timerIdRef.current)
}
}, []);
return active && <div>Loading...</div>;
}
/* Loader function component + higher order component that renders empty until a timeout
*
* HOC's have the following issues:
* - Outer ref masks the inner ref and therefore requires ref forwarding
* - Inner component static methods are hidden and require hoisting
* - HOC-injected props must be merged with pass-through props and could cause a name conflict
*/
function withDelay(Component) {
return React.forwardRef(function Delay(props, ref) {
const { timeoutMs, children } = props;
const [active, setActive] = React.useState(false);
const timerIdRef = React.useRef();
React.useEffect(() => {
timerIdRef.current = setTimeout(() => {
setActive(true);
}, props.timeoutMs);
return () => {
clearTimeout(timerIdRef.current)
}
}, []);
return <Component {...props} active={active} ref={ref} />;
})
}
const LoaderWithDelay = withDelay(function Loader(props) {
const { active } = props;
return active && <div>Loading...</div>;
});
/* Loader function component + render prop helper component that renders empty until a timeout */
function Delay(props) {
const { timeoutMs, children } = props;
const [active, setActive] = React.useState(false);
const timerIdRef = React.useRef();
React.useEffect(() => {
timerIdRef.current = setTimeout(() => {
setActive(true);
}, props.timeoutMs);
return () => {
clearTimeout(timerIdRef.current)
}
}, []);
return children({ active });
}
function LoaderWithDelay(props) {
const { timeoutMs } = props;
return (
<Delay timeoutMs={timeoutMs}>
{({ active }) => active && <div>Loading...</div>}
</Delay>
);
}
/* Loader function component + custom hook that renders empty until a timeout */
function useDelay({ timeoutMs }) {
const [active, setActive] = React.useState(false);
const timerIdRef = React.useRef();
React.useEffect(() => {
timerIdRef.current = setTimeout(() => {
setActive(true);
}, timeoutMs);
return () => {
clearTimeout(timerIdRef.current)
}
}, []);
return active;
}
function LoaderWithDelay(props) {
const { timeoutMs } = props;
const active = useDelay({ timeoutMs });
return active && <div>Loading...</div>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment