Last active
May 21, 2021 13:30
-
-
Save ianmstew/c271ae8ba4b0afd9d580d59ae26f1f05 to your computer and use it in GitHub Desktop.
HOC vs Render Prop vs Custom Hook
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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