How would you use suspend-react when fetching through a hook?
vbylen opened this issue · 4 comments
vbylen commented
For example with a usePostData
hook:
const Post = ({ postId }) => {
const { title } = usePostData(postId)
return (
<Text>{title}</Text>
)
}
Any insights would be greatly appreciated, thanks!
drcmda commented
what does usePostData do? where does it come from?
vbylen commented
@drcmda In my project I have it setup like this:
import { useEffect, useState } from 'react'
import { supabase } from '../../lib/initSupabase'
import { useStore } from '../store/Root'
const usePostData = (post_id) => {
const { postsStore, usersStore } = useStore()
const [loading, setLoading] = useState(true)
const [post, setPost] = useState(null)
const [author, setAuthor] = useState(null)
const [recipient, setRecipient] = useState(null)
const fetchPostData = async () => {
if (!post_id)
return {
post: null,
author: null,
recipient: null
}
try {
let response = {
post: null,
author: null,
recipient: null
}
const postData = await supabase
.from('posts')
.select(`*, author:author_id(*), recipient:recipient_id(*) `)
.eq('post_id', post_id)
if ((postData.data && postData.data.length > 0) || !postData.error) {
response.post = {
caption: postData.data[0]?.caption,
image_url: postData.data[0]?.image_url,
post_id: postData.data[0].post_id,
posted_at: postData.data[0].posted_at,
author_id: postData.data[0].author_id,
title: postData.data[0].title,
text: postData.data[0].text,
}
response.author = {
avatar_url: postData.data[0]?.author?.avatar_url,
bio: postData.data[0]?.author?.bio,
confirmed_at: postData.data[0]?.author?.confirmed_at,
email: postData.data[0]?.author?.email,
firstname: postData.data[0]?.author?.firstname,
id: postData.data[0]?.author?.id,
lastname: postData.data[0]?.author?.lastname,
phone: postData.data[0]?.author?.phone,
tag: postData.data[0]?.author?.tag,
website: postData.data[0]?.author?.website,
}
response.recipient = {
avatar_url: postData.data[0]?.recipient?.avatar_url,
bio: postData.data[0]?.recipient?.bio,
confirmed_at: postData.data[0]?.recipient?.confirmed_at,
email: postData.data[0]?.recipient?.email,
firstname: postData.data[0]?.recipient?.firstname,
id: postData.data[0]?.recipient?.id,
lastname: postData.data[0]?.recipient?.lastname,
phone: postData.data[0]?.recipient?.phone,
tag: postData.data[0]?.recipient?.tag,
website: postData.data[0]?.recipient?.website,
}
} else {
console.error(
`[fetchPostData_Response]
${postData.error || 'No Data'}`
)
return {
post: null,
author: null,
recipient: null
}
}
return response
} catch (err) {
console.error('[fetchPostData]', err)
return {
post: null,
author: null,
recipient: null
}
}
}
const fetchPost = async () => {
const postData = await fetchPostData()
if (postData?.post) setPost(postData.post)
if (postData?.author) setAuthor(postData.author)
if (postData?.recipient) setAuthor(postData.recipient)
setLoading(false)
}
useEffect(() => {
if (post_id) fetchPost()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [post_id])
useEffect(() => {
if (post_id) {
const postData = postsStore.posts.find(
(postToFind) => postToFind.post_id === post_id
)
if (postData) {
setPost(postData)
const userData = usersStore.users.find(
(userToFind) => userToFind.id === postData.author_id
)
if (userData) setAuthor(userData)
const recipientData = usersStore.users.find(
(userToFind) => userToFind.id === postData.recipient_id
)
if (recipientData) setRecipient(recipientData)
}
}
}, [post_id])
if (!post_id)
return {
fetchPost,
loading: false,
post: null,
author: null,
recipient: null
}
return {
fetchPost,
loading,
post,
author,
recipient
}
}
export default usePostData
drcmda commented
error handling and loading state move to the parental level, so that burden goes away. let supabase crash and it's going to get caught. you don't need to mock, check or validate, the data will be there.
import suspend from 'suspend-react'
import { ErrorBoundary } from 'react-error-boundary'
import { supabase } from '../../lib/initSupabase'
import { useStore } from '../store/Root'
export function usePostData(post_id) {
const { postsStore, usersStore } = useStore()
return suspend(async () => {
const postData = await supabase
.from('posts')
.select('*, author:author_id(*), recipient:recipient_id(*)')
.eq('post_id', post_id)
return postData.data[0]
}, [post_id])
}
function Post({ postId }) {
const { title, author: { name }, recipient: { phone } } = usePostData(postId)
return <div>{title}, {name}, {phone}</div>
}
function App() {
<Suspense fallback={<div>loading ...</div>}>
<ErrorBoundary fallback={<div>error ...</div>}>
<Post postId={1} />
</ErrorBoundary>
</Suspense>
vbylen commented
Super clean, thanks!