UNPKG

@layr/react-integration

Version:
385 lines (384 loc) 15.4 kB
import { ObservableType } from '@layr/observable'; import { DependencyList } from 'react'; import { AsyncFunction } from 'core-helpers'; import { Customization } from './components'; /** * A convenience hook for loading data asynchronously and rendering a React element using the loaded data. * * The `getter()` asynchronous function is called when the React component is rendered for the first time and when a change is detected in its `dependencies`. * * While the `getter()` function is running, the `useData()` hook returns the result of the nearest `dataPlaceholder()` function, which can be defined in any parent component thanks to the [`Customizer`](https://layrjs.com/docs/v2/reference/react-integration#customizer-react-component) component. * * Once the `getter()` function is executed, the `useData()` hook returns the result of the `renderer()` function which is called with the result of the `getter()` function as first parameter. * * If an error occurs during the `getter()` function execution, the `useData()` hook returns the result of the nearest `errorRenderer()` function, which can be defined in any parent component thanks to the [`Customizer`](https://layrjs.com/docs/v2/reference/react-integration#customizer-react-component) component. * * @param getter An asynchronous function for loading data. * @param renderer A function which is called with the result of the `getter()` function as first parameter and a `refresh()` function as second parameter. You can call the `refresh()` function to force the re-execution of the `getter()` function. The `renderer()` function should return a React element (or `null`). * @param [dependencies] An array of values on which the `getter()` function depends (default: `[]`). * @param [options.dataPlaceholder] A custom `dataPlaceholder()` function. * @param [options.errorRenderer] A custom `errorRenderer()` function. * * @returns A React element (or `null`). * * @example * ``` * import {Component} from '﹫layr/component'; * import React from 'react'; * import {view, useData} from '﹫layr/react-integration'; * * class Article extends Component { * // ... * * ﹫view() static List() { * return useData( * async () => { * // Return some articles from the backend * }, * * (articles) => { * return articles.map((article) => ( * <div key={article.id}>{article.title}</div> * )); * } * ); * } * } * ``` * * @category High-Level Hooks * @reacthook */ export declare function useData<Result>(getter: () => Promise<Result>, renderer: (data: Result, refresh: () => void) => JSX.Element | null, deps?: DependencyList, options?: { dataPlaceholder?: Customization['dataPlaceholder']; errorRenderer?: Customization['errorRenderer']; }): JSX.Element | null; /** * A convenience hook for executing some asynchronous actions. * * The specified `handler()` asynchronous function is wrapped so that: * * - When running, the screen is locked to prevent the user from interacting with any UI element. You can customize the screen locking mechanism in any parent component thanks to the [Customizer's `actionWrapper()`](https://layrjs.com/docs/v2/reference/react-integration#customizer-react-component) prop. * - In case an error is thrown, an error alert dialog is displayed. You can customize the error alert dialog in any parent component thanks to the [Customizer's `errorNotifier()`](https://layrjs.com/docs/v2/reference/react-integration#customizer-react-component) prop. * * @param handler An asynchronous function implementing the action. * @param [dependencies] An array of values on which the `handler()` function depends (default: `[]`). * @param [options.actionWrapper] A custom `actionWrapper()` function. * @param [options.errorNotifier] A custom `errorNotifier()` function. * * @returns An asynchronous function wrapping the specified `handler()`. * * @example * ``` * import {Component} from '﹫layr/component'; * import React from 'react'; * import {view, useAction} from '﹫layr/react-integration'; * * class Article extends Component { * // ... * * ﹫view() EditView() { * const save = useAction(async () => { * // Save the edited article to the backend * }); * * return ( * <form * onSubmit={(event) => { * event.preventDefault(); * save(); * }} * > * <div> * Implement your form fields here. * </div> * * <div> * <button type="submit">Save</button> * </div> * </form> * ); * } * } * ``` * * @category High-Level Hooks * @reacthook */ export declare function useAction<Args extends any[] = any[], Result = any>(handler: AsyncFunction<Args, Result>, deps?: DependencyList, options?: { actionWrapper?: Customization['actionWrapper']; errorNotifier?: Customization['errorNotifier']; }): (...args: Args) => Promise<Result>; /** * Makes a view dependent of an [observable](https://layrjs.com/docs/v2/reference/observable#observable-type) so the view is automatically re-rendered when the observable changes. * * @param observable An [observable](https://layrjs.com/docs/v2/reference/observable#observable-type) object. * * @example * ``` * import {Component} from '﹫layr/component'; * import {createObservable} from '﹫layr/observable'; * import React from 'react'; * import {view, useObserve} from '﹫layr/react-integration'; * * const observableArray = createObservable([]); * * class MyComponent extends Component { * ﹫view() static View() { * useObserve(observableArray); * * return ( * <div> * {`observableArray's length: ${observableArray.length}`} * </div> * ); * } * } * * // Changing `observableArray` will re-render `MyComponent.View` * observableArray.push('abc'); * ``` * * @category High-Level Hooks * @reacthook */ export declare function useObserve(observable: ObservableType): void; /** * Allows you to define an asynchronous callback and keep track of its execution. * * Plays the same role as the React built-in [`useCallback()`](https://reactjs.org/docs/hooks-reference.html#usecallback) hook but works with asynchronous callbacks. * * @param asyncCallback An asynchronous callback. * @param [dependencies] An array of values on which the asynchronous callback depends (default: `[]`). * * @returns An array of the shape `[trackedCallback, isExecuting, error, result]` where `trackedCallback` is a function that you can call to execute the asynchronous callback, `isExecuting` is a boolean indicating whether the asynchronous callback is being executed, `error` is the error thrown by the asynchronous callback in case of failed execution, and `result` is the value returned by the asynchronous callback in case of succeeded execution. * * @example * ``` * import {Component} from '﹫layr/component'; * import React from 'react'; * import {view, useAsyncCallback} from '﹫layr/react-integration'; * * class Article extends Component { * ﹫view() UpvoteButton() { * const [handleUpvote, isUpvoting, upvotingError] = useAsyncCallback(async () => { * await this.upvote(); * }); * * return ( * <div> * <button onClick={handleUpvote} disabled={isUpvoting}>Upvote</button> * {upvotingError && ' An error occurred while upvoting the article.'} * </div> * ); * } * } * ``` * * @category Low-Level Hooks * @reacthook */ export declare function useAsyncCallback<Args extends any[] = any[], Result = any>(asyncCallback: AsyncFunction<Args, Result>, deps?: DependencyList): readonly [(...args: Args) => Promise<Result>, boolean, any, Result | undefined]; /** * Memoizes the result of an asynchronous function execution and provides a "recompute function" that you can call to recompute the memoized result. * * The asynchronous function is executed one time when the React component is rendered for the first time, and each time a dependency is changed or the "recompute function" is called. * * Plays the same role as the React built-in [`useMemo()`](https://reactjs.org/docs/hooks-reference.html#usememo) hook but works with asynchronous functions and allows to recompute the memoized result. * * @param asyncFunc An asynchronous function to compute the memoized result. * @param [dependencies] An array of values on which the memoized result depends (default: `[]`, which means that the memoized result will be recomputed only when the "recompute function" is called). * * @returns An array of the shape `[memoizedResult, isExecuting, error, recompute]` where `memoizedResult` is the result returned by the asynchronous function in case of succeeded execution, `isExecuting` is a boolean indicating whether the asynchronous function is being executed, `error` is the error thrown by the asynchronous function in case of failed execution, and `recompute` is a function that you can call to recompute the memoized result. * * @example * ``` * import {Component} from '﹫layr/component'; * import React from 'react'; * import {view, useAsyncMemo} from '﹫layr/react-integration'; * * class Article extends Component { * // ... * * ﹫view() static List() { * const [articles, isLoading, loadingError, retryLoading] = useAsyncMemo( * async () => { * // Return some articles from the backend * } * ); * * if (isLoading) { * return <div>Loading the articles...</div>; * } * * if (loadingError) { * return ( * <div> * An error occurred while loading the articles. * <button onClick={retryLoading}>Retry</button> * </div> * ); * } * * return articles.map((article) => ( * <div key={article.id}>{article.title}</div> * )); * } * } * ``` * * @category Low-Level Hooks * @reacthook */ export declare function useAsyncMemo<Result>(asyncFunc: () => Promise<Result>, deps?: DependencyList): readonly [Result | undefined, boolean, any, () => void]; /** * Memoizes the result of a function execution and provides a "recompute function" that you can call to recompute the memoized result. * * The function is executed one time when the React component is rendered for the first time, and each time a dependency is changed or the "recompute function" is called. * * Plays the same role as the React built-in [`useMemo()`](https://reactjs.org/docs/hooks-reference.html#usememo) hook but with the extra ability to recompute the memoized result. * * @param func A function to compute the memoized result. * @param [dependencies] An array of values on which the memoized result depends (default: `[]`, which means that the memoized result will be recomputed only when the "recompute function" is called). * * @returns An array of the shape `[memoizedResult, recompute]` where `memoizedResult` is the result of the function execution, and `recompute` is a function that you can call to recompute the memoized result. * * @example * ``` * import {Component, provide} from '﹫layr/component'; * import React, {useCallback} from 'react'; * import {view, useRecomputableMemo} from '﹫layr/react-integration'; * * class Article extends Component { * // ... * } * * class Blog extends Component { * ﹫provide() static Article = Article; * * ﹫view() static CreateArticleView() { * const [article, resetArticle] = useRecomputableMemo(() => new Article()); * * const createArticle = useCallback(async () => { * // Save the created article to the backend * resetArticle(); * }, [article]); * * return ( * <div> * <article.CreateForm onSubmit={createArticle} /> * </div> * ); * } * } * ``` * * @category Low-Level Hooks * @reacthook */ export declare function useRecomputableMemo<Result>(func: () => Result, deps?: DependencyList): readonly [Result, () => void]; /** * Allows you to call an asynchronous function and keep track of its execution. * * The function is executed one time when the React component is rendered for the first time, and each time a dependency is changed or the "recall function" is called. * * @param asyncFunc The asynchronous function to call. * @param [dependencies] An array of values on which the asynchronous function depends (default: `[]`, which means that the asynchronous will be recalled only when the "recall function" is called). * * @returns An array of the shape `[isExecuting, error, recall]` where `isExecuting` is a boolean indicating whether the asynchronous function is being executed, `error` is the error thrown by the asynchronous function in case of failed execution, and `recall` is a function that you can call to recall the asynchronous function. * * @example * ``` * // JS * * import {Component, provide, attribute} from '﹫layr/component'; * import React from 'react'; * import {view, useAsyncCall} from '﹫layr/react-integration'; * * class Article extends Component { * // ... * } * * class Blog extends Component { * ﹫provide() static Article = Article; * * ﹫attribute('Article[]?') static loadedArticles; * * ﹫view() static View() { * const [isLoading, loadingError, retryLoading] = useAsyncCall( * async () => { * this.loadedArticles = await this.Article.find(); * } * ); * * if (isLoading) { * return <div>Loading the articles...</div>; * } * * if (loadingError) { * return ( * <div> * An error occurred while loading the articles. * <button onClick={retryLoading}>Retry</button> * </div> * ); * } * * return this.loadedArticles.map((article) => ( * <div key={article.id}>{article.title}</div> * )); * } * } * ``` * * @example * ``` * // TS * * import {Component, provide, attribute} from '﹫layr/component'; * import React from 'react'; * import {view, useAsyncCall} from '﹫layr/react-integration'; * * class Article extends Component { * // ... * } * * class Blog extends Component { * ﹫provide() static Article = Article; * * ﹫attribute('Article[]?') static loadedArticles?: Article[]; * * ﹫view() static View() { * const [isLoading, loadingError, retryLoading] = useAsyncCall( * async () => { * this.loadedArticles = await this.Article.find(); * } * ); * * if (isLoading) { * return <div>Loading the articles...</div>; * } * * if (loadingError) { * return ( * <div> * An error occurred while loading the articles. * <button onClick={retryLoading}>Retry</button> * </div> * ); * } * * return this.loadedArticles!.map((article) => ( * <div key={article.id}>{article.title}</div> * )); * } * } * ``` * * @category Low-Level Hooks * @reacthook */ export declare function useAsyncCall(asyncFunc: () => Promise<void>, deps?: DependencyList): readonly [boolean, any, () => void]; export declare function useIsMounted(): () => boolean; export declare function useForceUpdate(): () => void; export declare function useDelay(duration?: number): readonly [boolean];