Skip to content

vlad-zhukov/priem

Repository files navigation

priem · npm Build Status codecov bundlephobia

React Hook to declaratively subscribe to external data resources.

Table of Contents

Installation

yarn add priem

Getting Started

App.js

import React from 'react';
import {createResource} from 'priem';

const useReddit = createResource(
    () =>
        fetch('https://www.reddit.com/r/reactjs.json')
            .then(res => res.json())
            .then(res => res.data.children),
    {
        ssrKey: 'reddit-resource',
    },
);

export default () => {
    const [redditData, {pending}] = useReddit({});

    if (!redditData) {
        return pending ? <h2>Loading...</h2> : <h2>Empty.</h2>;
    }

    return (
        <div style={{opacity: pending ? 0.5 : 1}}>
            <ul>
                {redditData.map((post, i) => (
                    <li key={i}>{post.title}</li>
                ))}
            </ul>
        </div>
    );
};

server.js

import React from 'react';
import ReactDOM from 'react-dom/server';
import {getDataFromTree} from 'priem/server';
import {flushStore} from 'priem';
import App from './App';

app.get(async (req, res) => {
    await getDataFromTree(<App />);
    const content = ReactDOM.renderToString(<App />);

    // We suggest to use a specific library instead of JSON.stringify
    // for example `devalue` or `serialize-javascript`.
    const storeJson = JSON.stringify(flushStore()).replace(/</g, '\\u003c');

    res.send(`
        <!doctype html>
        ${ReactDOM.renderToStaticMarkup(<Html content={content} />)}
        <script id="preloaded-state">
            window.__PRIEM_STORE__ = ${storeJson};
        </script>
    `);
});

client.js

import React from 'react';
import ReactDOM from 'react-dom';
import {hydrateStore} from 'priem';

hydrateStore(JSON.parse(window.__PRIEM_STORE__));
delete window.__PRIEM_STORE__;

// Note that the import order is important here
const App = require('./App').default;

ReactDOM.hydrate(<App />, document.getElementById('root'));

Examples

Example apps can be found under the examples/ directory.

API

createResource

Creates a React Hook that fetches and caches data.

Arguments

  1. fn: (AsyncFunction): An async function that takes arguments from useResource and must return a Promise. If promise rejects, the cache item corresponding to these arguments will have a rejected status.
  2. options (Object): An options object, that can have the following properties:
    • maxAge (number?): A time in milliseconds after which cache items will expire and trigger a refresh.
    • maxSize (number?): A number of maximum cache entries in store. After exceeding this amount the most former used item will be removed and a refresh triggered. Defaults to 1.
    • refreshOnMount (Boolean?): Refreshes data on mounting. Default to false.
    • ssrKey (string?): A unique key that will be used to place this resource to the store. Required for server-side rendering.

Returns

useResource.

useResource

A React Hook for subscribing to resources.

Arguments

  1. args (Record<string, unknown> | null): An array of arguments that will be passed to a function in createResource. Can also be null which will prevent the update which can be utilized for waiting for other async tasks or user interactions to finish. Defaults to [].

Returns

The function returns a tuple with data and a meta object:

  1. data (any): The last successful data. Defaults to null.
  2. meta (Object): Meta properties of most recent promise.
    • pending (boolean).
    • rejected (boolean).
    • reason (Error?).
    • refresh (Function): a method to update the resource.

getDataFromTree(element)

An async function that walks the component tree and fetches data from resources that have ssrKey set. Returns a promise that either resolves with undefined or rejects on errors.


hydrateStore(initialStore)

A function to hydrate internal store with initial data from server.


flushStore()

A function that clears internal store and returns it. It's safe to serialize it and send to client.