In this post, I want to introduce you RenderProps
: what is it and how to use it. RenderProps
is a pattern, as well as HOC
(higher order component
) it's designed to pack logic into a component for further reuse where required.
Simply said, it is a component that takes props
, one of which must be a function. Calling this function, we can pass the data by arguments that will be available in the component from which the function was passed.
You can transfer data from the involving component to the
RenderProps
component via props. In aHOC
case, we can only pass static data as arguments.
Short description: ({ children }) => { logic... return children(args) }
Imagine we have two pages where each needs to fetch user data. The logic for each page is repeating so we will create a ProvideAuth
component which provides the user profile and loading
state.
const ProvideAuth = ({ children }) => {
// state
const [userProfile, setUserProfile] = React.useState({ isAuthorized: false, data: {} })
const [isUserLoading, setUserLoadingState] = React.useState(false)
const handleSetUserLoading = value => {
setUserLoadingState(value)
}
React.useEffect(() => {
handleGetUser()
}, [])
const handleGetUser = async () => {
try {
handleSetUserLoading(true)
const response = await getUser()
setUserProfile({ isAuthorized: true, data: response.data })
} catch (error) {
console.log('Error while User preload:', error)
} finally {
handleSetUserLoading(false)
}
}
if (!userProfile.isAuthorized && !isUserLoading) {
return <div>U're not authorized</div>
}
return (
<>
{isUserLoading ? (
<div>Loading...</div>
) : (
<>
{/* call children function and provide data */}
{children({ userProfile, isUserLoading })}
</>
)}
</>
)
}
const PageFirst = () => (
<ProvideAuth>
{/* provide a function as children and return ReactNode */}
{({ userProfile }) => (
<>
<div>Your First Name: {userProfile.data.firstName}</div>
<div>Your Last Name: {userProfile.data.lastName}</div>
<div>Is Authorized: {userProfile.isAuthorized ? 'Yes' : 'No'}</div>
</>
)}
</ProvideAuth>
)
const PageSecond = () => (
<ProvideAuth>
{/* provide a function as children and return ReactNode */}
{({ userProfile }) => (
<div>
Your Full Name: {userProfile.data.firstName} {userProfile.data.lastName}
</div>
)}
</ProvideAuth>
)
If RenderProps
(ProvideAuth
) wraps elements within render, a function that returns children
is specified instead of children
as ReactNode(s)
. The data passed from the ProvideAuth are the arguments for this function. Thus, unlike a standard container where children
can be ReactNode(s)
, we pass a function, once called, returns a ReactNode
. That 's all the magic RenderProps
.
Instead of children
, we can pass the function as props
with a common name and return the ReactNode
as well.
const ProvideAuth = ({ renderAuth }) => {
// state
const [userProfile, setUserProfile] = React.useState({ isAuthorized: false, data: {} })
const [isUserLoading, setUserLoadingState] = React.useState(false)
const handleSetUserLoading = value => {
setUserLoadingState(value)
}
React.useEffect(() => {
handleGetUser()
}, [])
const handleGetUser = async () => {
try {
handleSetUserLoading(true)
const response = await getUser()
setUserProfile({ isAuthorized: true, data: response.data })
} catch (error) {
console.log('Error while User preload:', error)
} finally {
handleSetUserLoading(false)
}
}
if (!userProfile.isAuthorized && !isUserLoading) {
return <div>U're not authorized</div>
}
return (
<>
{isUserLoading ? (
<div>Loading...</div>
) : (
<>
{/* call renderAuth prop function and provide data */}
{renderAuth({ userProfile, isUserLoading })}
</>
)}
</>
)
}
const PageFirst = () => (
<ProvideAuth
// provide prop renderAuth function and return ReactNode
renderAuth={({ userProfile }) => (
<>
<div>Your First Name: {userProfile.data.firstName}</div>
<div>Your Last Name: {userProfile.data.lastName}</div>
<div>Is Authorized: {userProfile.isAuthorized ? 'Yes' : 'No'}</div>
</>
)}
/>
)
const PageSecond = () => (
<ProvideAuth
// provide prop renderAuth function and return ReactNode
renderAuth={({ userProfile }) => (
<div>
Your Full Name: {userProfile.data.firstName} {userProfile.data.lastName}
</div>
)}
/>
)
As a matter of experience, I can say RenderProps
is ideal for creating UI
modules that can be reused in different projects. They can be easily adapted to the needs of each project where applicable. That is very important for development acceleration.