Async ReactJS components through composition.
Imagine we had a ChatSDK
, and we wanted to use it to load messages from a conversation between two users using React
. We would love to do something like the following:
async function App() {
await ChatSDK.connect()
const channel = await ChatSDK.getChannel()
const messages = await channel.getMessages()
return <Messages messages={messages} />
}
Unfortunately, using async-await inside React components is not possible without some work arounds, rightfully so because this would end up in really complex rendering logic and components.
For example, what would React render while it is waiting for the chat to connect? I assume React Suspense will solve this but as it is still not fully out —at the moment of writing—, this is still an issue.
One way to fix this using composition:
function App() {
return (
<ChatConnect>
<ChatChannel>
{channel => (
<ChatMessages channel={channel} />
}
</ChatChannel>
</ChatConnect>
)
}function ChatConnect({ children }) {
const [status, setStatus] = React.useState('disconnected')
React.useEffect(() => {
ChatSDK.connect().then(status => setStatus(status))
}, [])
if(status !== 'connected') {
return 'connecting...'
}
return <>{children}</>
}function ChatChannel({ children }) {
const [channel, setChannel] = React.useState(null)
React.useEffect(() => {
ChatSDK.getChannel().then(channel => setChannel(channel))
}, [])
if(!channel) {
return 'loading channel...'
}
return <>{children(channel)}</>
}function ChatMessages({ channel }) {
const [messages, setMessages] = React.useState(null)
React.useEffect(() => {
channel.getMessages().then(messages => setMessages(messages))
}, [channel])
if(!messages) {
return 'loading messages...'
}
return <Messages messages={messages} />
}
We can clearly see this is more verbose than expected, but it offers full control of the rendering process, enhancing user experience. It also is fully scalable as we can keep composing and wrapping components as needed. Lastly, this method manages resources correctly as there are no unnecessary re-renders which depending on what is being rendered could cause the UI to lag.
Another way to solve this is by separating the async-await process and use a custom component to load all at once:
Thanks.