Skip to content

Releases: stegano/react-render-state-hook

v1.2.4

07 Feb 06:54
Compare
Choose a tag to compare

Docs

  • docs(README.md): add API Docs section
  • docs(APIs): add APIs docs

v1.2.3

01 Feb 12:57
Compare
Choose a tag to compare

Fixes

  • fix(providers): resolve issue where child components are re-rendered by RenderStateProvider

v1.2.2

12 Jan 13:02
Compare
Choose a tag to compare

Fixes

  • fix(hooks): rename variables
  • fix(hooks): change dataHandlerExecutor function type
  • fix(hooks): resolve issue that initial data and status are not set properly

v1.2.0

06 Jan 11:40
Compare
Choose a tag to compare

Features

  • feat(store): add debug option
  • feat(contexts): add store property to RenderSateProvider

Fixes

  • fix(store): add initialStore option

BREAKING CHANGES

  • fix(providers): rename the dataHandlerExecutorInterceptors option to dataHandlerExecutorInterceptorList

  • fix(store): change middleware interface return type

    // Before
    export interface Middleware<Data, Dataset = Record<string, Data>> {
        (id: string, next: Data, store: Dataset): { id: string; data: Data } | undefined;
    }
    // After
    export interface Middleware<Data, Dataset = Record<string, Data>> {
        (id: string, next: Data, store: Dataset): Data;
    }

v1.1.1

04 Jan 16:14
Compare
Choose a tag to compare

Docs

  • docs(README.md): update source code examples

v1.1.0

04 Jan 16:06
Compare
Choose a tag to compare

Features

  • feat(hooks): add DataResetHandler function
  • feat(hooks): add Idle status

Refactors

  • refactor(hooks): rename interfaces and add descriptions
  • refactor(hooks): rename DataHandleStatus

BREAKING CHANGES

  • The DataHandlingStatus enum values have been modified. Please refer to the code snippet below:

    // Before
    export enum DataHandlingStatus {
      IN_PROGRESS = "IN_PROGRESS",
      SUCCEEDED = "SUCCEEDED",
      FAILED = "FAILED",
    }
    // After
    export enum DataHandlingStatus {
      IDLE = "IDLE",
      IN_PROGRESS = "IN_PROGRESS",
      COMPLETED = "COMPLETED",
      FAILURE = "FAILURE",
    }
  • With the addition of DataResetHandler, the return value of the useRenderState hook has been changed. Please refer to the code snippet below:

    // Before
    const [render, handleData, state] = useRenderState(...);
    // After
    const [render, handleData, handleDataReset, state] = useRenderState(...);
  • Due to the addition of the Idle state, the render argument has been modified. Please refer to the code snippet below:

    const [render] = useRenderState(...);
    ...
    // Before
    return render("Completed", "Loading", "Failure");
    // After
    return render("Completed", "Idle", "Loading", "Failure");

v1.0.0

25 Dec 11:14
Compare
Choose a tag to compare

React Render State Hook

NPM License NPM Downloads

React Render State Hook: This hook allows you to declaratively define components that will be rendered based on the data processing state.

Installation

The easiest way to install react-render-state-hook is with npm.

npm install react-render-state-hook

Alternately, download the source.

git clone https://github.com/stegano/react-render-state-hook.git

Quick Start

Basic

The useRenderState hook enables a declarative approach to display components based on data processing status.

import { useState, useCallback } from "react";
import { useRenderState } from "react-render-state-hook";

export const App = () => {
  const [render, handleData] = useRenderState<string, Error>();

  // When this function is invoked, it generates and returns a random string after 3 seconds.
  const updateRandomString = useCallback(
    () =>
      new Promise<string>((resolve) => {
        const randomString = Math.random().toString(32).slice(2);
        setTimeout(() => resolve(randomString), 1000 * 3);
      }),
    [],
  );

  useEffect(() => {
    // When the component is first rendered, it updates a random string.
    handleData(async () => updateRandomString());
  }, [updateRandomString, handleData]);

  const handleButtonClick = useCallback(() => {
    // When the button is clicked, it updates a random string.
    handleData(async () => updateRandomString());
  }, [updateRandomString, handleData]);

  // Use `render` function to define rendering for data processing statuses: succeeded, in-progress, or failed. It auto-renders based on the `handleData` function's processing status.
  return render(
    (data) => (
      <div>
        <p>Succeeded({data})</p>
        <button type="button" onClick={handleButtonClick}>
          Update
        </button>
      </div>
    ),
    <p>Loading..</p>,
    (error) => <p>Error, Oops something went wrong.. :(, ({error.message})</p>,
  );
};

Demo: https://stackblitz.com/edit/stackblitz-starters-pgefl6

Sharing Rendering Data

It is possible to share data and rendering state among multigitple containers(components).

import { useState, useCallback } from "react";
import { useRenderState } from "react-render-state-hook";

export const ComponentA = () => {
  const [render, handleData] = useRenderState<string, Error>(
    undefined, 
    "randomString" // By providing a data sharing key, you can share data processing state and values.
  );
  
  const updateRandomString = useCallback(
    () =>
      new Promise<string>((resolve) => {
        const randomString = Math.random().toString(32).slice(2);
        setTimeout(() => resolve(randomString), 1000 * 3);
      }),
    [],
  );

  useEffect(() => {
    handleData(async () => updateRandomString());
  }, [updateRandomString, handleData]);

  return render(
    (data) => (
      <div>
        <p>Succeeded({data})</p>
      </div>
    ),
    <p>Loading..</p>,
    (error) => <p>Error, Oops something went wrong.. :(, ({error.message})</p>,
  );
};

export const ComponentB = () => {
  const [render] = useRenderState<string, Error>(
    undefined, 
    "randomString" // By providing a data sharing key, you can share data processing state and values.
  );

  // While this component does not directly handle the data, the rendering data state is updated by ComponentA.
  return render(
    (data) => (
      <div>
        <p>Succeeded({data})</p>
      </div>
    ),
    <p>Loading..</p>,
    (error) => <p>Error, Oops something went wrong.. :(, ({error.message})</p>,
  );
};

export const App = () => {
  return (
    <>
      <ComponentA />
      <ComponentB />
    </>
  )
};

🧐 Advanced features

dataHandlerExecutorInterceptors

dataHandlerExecutorInterceptors can intercept dataHandlerExecutor execution, allowing you to transform it. It can be useful for adding logs for data processing or injecting dummy data for use in Storybook and testing environments.

import { useCallback, useEffect } from 'react';
import {
  RenderStateProvider,
  useRenderState,
} from 'react-render-state-hook';

const Component = () => {
  const generateGreetingMessage = useCallback(async () => {
    // e.g., asynchronous processing tasks like `return (await axios.get('.../data.json')).data.greeting;` are also possible.
    return 'Hi';
  }, []);

  const [render, handleData] = useRenderState<string>();

  useEffect(() => {
    handleData(async () => {
        const greeting = await generateGreetingMessage();
        return greeting;
      }, 
      'greeting' // 'greeting' is the executorId. This value serves as an identifier in `dataHandlerExecutorInterceptors` to distinguish tasks.
    );
  }, [handleData]);

  return render((greeting) => <p>{greeting}</p>);
};

export const App = ({ children }) => {
  return (
    <RenderStateProvider
      dataHandlerExecutorInterceptors={[
        async (_previousInterceptorResult, dataHandlerExecutor, executorId) => {
          if (executorId === 'greeting') {
            // The `dataHandlerExecutor` with an executorId value of 'greeting' is not actually executed instead, this provider returns the value 'Hello'.
            return 'Hello';
          }
          return await dataHandlerExecutor();
        }
      ]}
    >
      <Component />
    </RenderStateProvider>
  );
};

Demo: https://stackblitz.com/edit/stackblitz-starters-4qxzui