- Only 600B minified and gziped
- Simple hooks API
- TypeScript
- Can handle updates
- Simple cache
- Suspense on server side via
react-ssr-prepassπ
This is a NO BULLSHIT hook: just PLUG IT in your components, get ALL THE DATA you need (and some more) both CLIENT- and SERVER-side, HYDRATE that
bastardapp while SSRing like it's NO BIG DEAL, effortlessly PASS IT to the client and render THE SHIT out of it
$ npm i -S react-universal-data$ yarn add react-universal-dataRequests data and preserves the result to the state.
typeuseFetchData<T>=(// async function that can return any type of datafetcher: (key: string,context: {isServer: boolean})=>Promise<T>,// unique key that will be used for storing & hydrating data while SSRkey: string,// use cached value for specified duration of time, by default it will be requested each timettl?: number)=>AsyncState<T>
β οΈ Thekeymust be unique for the whole application.
Returned object can be in 4 different forms β depending on the promise's state.
exporttypeAsyncState<T>=// initial|{isReady: false;isLoading: false;error: null;result: undefined}// fulfilled|{isReady: true;isLoading: false;error: null;result: T}// pending|{isReady: boolean;isLoading: true;error: Error|null;result?: T}// rejected|{isReady: false;isLoading: false;error: Error;result?: T}π Fetch a sample post via jsonplaceholder.typicode.com API
importReactfrom'react'import{useFetchData}from'react-universal-data'constfetchPost=(id)=>fetch(`https://jsonplaceholder.typicode.com/posts/${id}`).then((response)=>response.json())functionPost({ id }){const{ isReady, isLoading, result, error }=useFetchData(fetchPost,id)if(isLoading){return<p>Loading...</p>}if(error){return<p>Oh no: {error.message}</p>}// You can depend on `isReady` flag to ensure data loaded correctlyif(isReady){return(<article><h2>{result.title}</h2><p>{result.body}</p></article>)}returnnull}As the hook depends on the fetcher function identity to be stable, please, wrap it inside useCallback or define it outside of the render function to prevent infinite updates.
importReact,{useCallback}from'react'import{useFetchData}from'react-universal-data'functionUserPosts({ userId }){constfetchPosts=useCallback(()=>(fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`).then((response)=>response.json())),[userId])// will pereform update if value changedconst{ result =[]}=useFetchData(fetchPosts,'user-posts')return(<ul>{result.map((post)=><likey={post.id}>{post.title}</li>)}</ul>)}π Create a custom hook for it
importReact,{useCallback}from'react'import{useFetchData}from'react-universal-data'functionuseFetchUserPosts(userId){returnuseFetchData(useCallback(()=>(fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`).then((response)=>response.json())),[userId]),'user-posts')}functionUserPosts({ userId }){const{ result =[]}=useFetchUserPosts(userId)return(<ul>{result.map((post)=><likey={post.id}>{post.title}</li>)}</ul>)}Handles useFetchData on server side and gathers results for hydration in the browser.
typegetInitialData=(element: JSX.Element)=>Promise<[string,any][]>// server.jsimportReactfrom'react'import{renderToString}from'react-dom/server'import{getInitialData}from'react-universal-data'import{App}from'./App'asyncfunctionserver(req,res){constelement=<App/>constdata=awaitgetInitialData(element).catch((error)=>/* handle error */)consthtml=renderToString(<html><body><divid='app'>{element}</div><scriptdangerouslySetInnerHTML={{__html: `window._ssr = ${JSON.stringify(data)};`,}}/><scriptsrc='/client.js'/></body></html>)res.write('<!DOCTYPE html>')res.write(html)res.end()}Hydrates initial data gathered with getInitialData before rendering the app in the browser.
typehydrateInitialData=(initial: [string,any][])=>void// client.jsimportReactfrom'react'importReactDOMfrom'react-dom'import{hydrateInitialData}from'react-universal-data'import{App}from'./App'hydrateInitialData(window._ssr||[])ReactDOM.hydrate(<App/>,document.getElementById('app'))react-ssr-prepass- server-side dependencyya-fetch- a lightweight wrapper aroundfetch
MIT Β© John Grishin