Render HOC Can be used for... To avoid multiple conditional statements for conditional component render Easier for eyes to catch
import { ReactElement } from 'react'
type Props = {
when: boolean
children: ReactElement
}
/**
* HOC for conditional rendering, can be used instead of combination of logical statements
* @param props props
* @param props.when condition for a component render
* @param props.children component to render
*/
export const Render = ({ when, children }: Props) => when ? children : null
function RenderBasic() {
const [show, setShow] = useState(true)
return (
<div>
<div>
<button onClick={() => setShow(!show)}>Show: {show.toString()}</button>{' '}
</div>
<Render when={show} >
<div css={{ fontSize: '2rem' }}>Render basic</div>
</Render>
</div>
)
}
Render with animation via GSAP That is my stupid creation
const RenderWithGsap = ({ when, children, withAnimation }) => {
const ref = useRef(null)
const [shouldRender, setShouldRender] = useState(when)
function slideIn() {
gsap.fromTo(
ref.current,
{ scaleY: 0, height: 0, opacity: 0, transformOrigin: '0 0' },
{ duration: 0.3, scaleY: 1, opacity: 1, height: 'auto', onComplete: () => null }
)
}
function slideOut() {
gsap.fromTo(
ref.current,
{ scaleY: 1, opacity: 1, transformOrigin: '0 0' },
{ duration: 0.3, scaleY: 0, opacity: 0, height: 0, onComplete: () => setShouldRender(false) }
)
}
useUpdateEffect(() => {
if (!withAnimation) setShouldRender(when)
}, [when])
useUpdateEffect(() => {
if (!withAnimation) return
when ? setShouldRender(true) : slideOut()
}, [when])
useUpdateEffect(() => {
if (!withAnimation) return
if (!shouldRender) return
slideIn()
}, [shouldRender])
if (!withAnimation) {
return shouldRender ? children : null
}
if (withAnimation) {
return shouldRender ? <div ref={ref}>{children}</div> : null
}
}
function RenderWithAnimationWithGsap() {
const [show, setShow] = useState(true)
return (
<div>
<div>
<button onClick={() => setShow(!show)}>Show: {show.toString()}</button>{' '}
</div>
<RenderWithGsap when={show} withAnimation >
<div css={{ fontSize: '2rem' }}>Render with animation via GSAP</div>
</RenderWithGsap>
</div>
)
Render without animation via GSAP
function RenderWithoutAnimationWithGsap() {
const [show, setShow] = useState(true)
return (
<div>
<div>
<button onClick={() => setShow(!show)}>Show: {show.toString()}</button>{' '}
</div>
<RenderWithGsap when={show} >
<div css={{ fontSize: '2rem' }}>Render without animation via GSAP</div>
</RenderWithGsap>
</div>
)
}
Render with MUI Collapse
const RenderWithCollapse = ({ when, children }) => {
return (
<Collapse in={when}>
{children}
</Collapse>
)
}
function RenderWithAnimationWithCollapse() {
const [show, setShow] = useState(true)
return (
<div>
<div>
<button onClick={() => setShow(!show)}>Show: {show.toString()}</button>{' '}
</div>
<RenderWithCollapse when={show} >
<div css={{ fontSize: '2rem' }}>Render with animation via MUI Collapse</div>
</RenderWithCollapse>
</div>
)
}
Render component is not the same as if statement
<>
Below is the render gate with falsy condition, which still executes the code inside:
<Render when={false}>
<div>
Some text
{window.alert('you still see me')}
</div>
</Render>
But normal condition does not execute the component function:
{
false && (
<div>
Some text
{window.alert('you do not see me')}
</div>
)
}
</>
Explanation for such behavior... Function arguments are evaluated inside round braces before function body execution. func(1 + 2) will be func(3) When we provide JSX into our <Render /> function, this JSX is evaluated as a parameter Because JSX it is a callable function itself React.createElement() behind its html-looking syntax Use basic if statement where possible, instead of <Render />