react-universal-data
TypeScript icon, indicating that this package has built-in type declarations

4.1.2 • Public • Published

🗂 react-universal-data

Easy to use hook for getting data on client and server side with effortless hydration of state

  • 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 bastard app while SSRing like it's NO BIG DEAL, effortlessly PASS IT to the client and render THE SHIT out of it

@razdvapoka

📦 Install

$ npm i -S react-universal-data
$ yarn add react-universal-data

📖 Docs

useFetchData

Requests data and preserves the result to the state.

type useFetchData<T> = (
  // async function that can return any type of data
  fetcher(key: string, context: { isServer: boolean }) => Promise<T>,
  // unique key that will be used for storing & hydrating data while SSR
  keystring,
  // use cached value for specified duration of time, by default it will be requested each time
  ttl?: number
) => AsyncState<T>

⚠️ The key must be unique for the whole application.

Returned object can be in 4 different forms – depending on the promise's state.

export type AsyncState<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
import React from 'react'
import { useFetchData } from 'react-universal-data'
 
const fetchPost = (id) =>
  fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
    .then((response) => response.json())
 
function Post({ 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 correctly
  if (isReady) {
    return (
      <article>
        <h2>{result.title}</h2>
        <p>{result.body}</p>
      </article>
    )
  }
 
  return null
}

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.

import React, { useCallback } from 'react'
import { useFetchData } from 'react-universal-data'
 
function UserPosts({ userId }) {
  const fetchPosts = useCallback(() => (
    fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)
      .then((response) => response.json())
  ), [userId]) // will pereform update if value changed
 
  const { result = [] } = useFetchData(fetchPosts, 'user-posts')
 
  return (
    <ul>
      {result.map((post) => <li key={post.id}>{post.title}</li>)}
    </ul>
  )
}
👀 Create a custom hook for it
import React, { useCallback } from 'react'
import { useFetchData } from 'react-universal-data'
 
function useFetchUserPosts(userId) {
  return useFetchData(
    useCallback(() => (
      fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)
        .then((response) => response.json())
    ), [userId]),
    'user-posts'
  )
}
 
function UserPosts({ userId }) {
  const { result = [] } = useFetchUserPosts(userId)
 
  return (
    <ul>
      {result.map((post) => <li key={post.id}>{post.title}</li>)}
    </ul>
  )
}

getInitialData

Handles useFetchData on server side and gathers results for hydration in the browser.

type getInitialData = (element: JSX.Element) => Promise<[string, any][]>
// server.js
import React from 'react'
import { renderToString } from 'react-dom/server'
import { getInitialData } from 'react-universal-data'
import { App } from './App'
 
async function server(req, res) {
  const element = <App />
 
  const data = await getInitialData(element).catch((error) => /* handle error */)
  const html = renderToString(
    <html>
      <body>
        <div id='app'>{element}</div>
        <script
          dangerouslySetInnerHTML={{
            __html: `window._ssr = ${JSON.stringify(data)};`,
          }}
        />
        <script src='/client.js' />
      </body>
    </html>
  )
 
  res.write('<!DOCTYPE html>')
  res.write(html)
  res.end()
}

hydrateInitialData

Hydrates initial data gathered with getInitialData before rendering the app in the browser.

type hydrateInitialData = (initial: [string, any][]) => void
// client.js
import React from 'react'
import ReactDOM from 'react-dom'
import { hydrateInitialData } from 'react-universal-data'
import { App } from './App'
 
hydrateInitialData(window._ssr || [])
ReactDOM.hydrate(<App />, document.getElementById('app'))

💻 Demo

Edit react-universal-data-ssr

🔗 Related

Packages

Real world usages


MIT © John Grishin

Package Sidebar

Install

npm i react-universal-data

Weekly Downloads

4

Version

4.1.2

License

MIT

Unpacked Size

35.8 kB

Total Files

35

Last publish

Collaborators

  • exah