{"id":20768146,"url":"https://github.com/softwaremill/react-use-promise-matcher","last_synced_at":"2025-04-30T11:24:20.308Z","repository":{"id":43279255,"uuid":"251245440","full_name":"softwaremill/react-use-promise-matcher","owner":"softwaremill","description":"React hooks allowing you to handle promises in a stateful way","archived":false,"fork":false,"pushed_at":"2023-08-30T00:59:06.000Z","size":1605,"stargazers_count":16,"open_issues_count":11,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-14T02:49:00.941Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/softwaremill.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-30T08:29:09.000Z","updated_at":"2024-02-09T19:53:23.000Z","dependencies_parsed_at":"2024-06-19T16:03:01.663Z","dependency_job_id":"76b7ecfe-84a9-4894-9b99-f2405d07cf03","html_url":"https://github.com/softwaremill/react-use-promise-matcher","commit_stats":{"total_commits":130,"total_committers":6,"mean_commits":"21.666666666666668","dds":0.3538461538461538,"last_synced_commit":"2daa1228a686041d4db950dc61182003aee22f62"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softwaremill%2Freact-use-promise-matcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softwaremill%2Freact-use-promise-matcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softwaremill%2Freact-use-promise-matcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/softwaremill%2Freact-use-promise-matcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/softwaremill","download_url":"https://codeload.github.com/softwaremill/react-use-promise-matcher/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251689382,"owners_count":21627901,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-17T11:35:54.200Z","updated_at":"2025-04-30T11:24:20.264Z","avatar_url":"https://github.com/softwaremill.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-use-promise-matcher\n\n## Contents\n\n-   [Installation](#installation)\n    -   [npm](#npm)\n    -   [yarn](#yarn)\n-   [About](#about)\n-   [Features](#features)\n    -   [Hooks](#hooks)\n        -   [usePromise](#usepromise)\n            -   [basic usage](#basic-usage)\n            -   [with arguments](#with-arguments)\n            -   [error handling](#error-handling)\n        -   [Polling: usePromiseWithInterval](#polling-usepromisewithinterval)\n    -   [Callback functions](#callback-functions)\n\n## Installation\n\n#### npm\n\n```bash\nnpm install react-use-promise-matcher\n```\n\n#### yarn\n\n```bash\nyarn add react-use-promise-matcher\n```\n\n## About\n\nThis library provides two hooks that aim to facilitate working with asynchronous data in React. Implementing components that depend on some data fetched from an API can generate a significant amount of boilerplate code as it is a common practice to handle the following situations:\n\n1. The data hasn't been loaded yet, so we want to display some kind of loading feedback to the user.\n2. Request failed with an error and we should handle this situation somehow.\n3. Request was successful and we want to render the component.\n\nUnfortunately, we cannot monitor the state of a `Promise` as it is a stateless object. We can keep the information about the status of the request within the component's state though, however this usually forces us to repeat the same code across many components.\n\nThe `usePromise` and `usePromiseWithArguments` hooks let you manage the state of a `Promise` without redundant boilerplate by using the `PromiseResultShape` object, which is represented by four states:\n\n-   `Idle` - a request hasn't been sent yet\n-   `Loading` - waiting for the `Promise` to resolve\n-   `Rejected` - the `Promise` was rejected\n-   `Resolved` - the `Promise` was resolved successfully\n\nThe `PromiseResultShape` provides an API that lets you match each of the states and perform some actions accordingly or map them to some value, which is the main use case, as we would normally map the states to different components. Let's have a look at some examples then ...\n\n## Features\n\n### Hooks\n\n#### usePromise\n\n##### Basic usage\n\nLet's assume we have a simple echo method that returns the string provided as an argument wrapped in a Promise.\nThis is how we would use the `usePromise` hook to render the received text based on what the method returns:\n\n```tsx\nconst echo = (text: string): Promise\u003cstring\u003e =\u003e new Promise((resolve) =\u003e setTimeout(() =\u003e resolve(text), 3000));\n\nexport const EchoComponent = () =\u003e {\n    const [result, load] = usePromise\u003cstring\u003e(() =\u003e echo(\"Echo!\"));\n\n    React.useEffect(() =\u003e {\n        load();\n    }, []);\n\n    return result.match({\n        Idle: () =\u003e \u003c\u003e\u003c/\u003e,\n        Loading: () =\u003e \u003cspan\u003eI say \"echo!\"\u003c/span\u003e,\n        Rejected: (err) =\u003e \u003cspan\u003eOops, something went wrong! Error: {err}\u003c/span\u003e,\n        Resolved: (echoResponse) =\u003e \u003cspan\u003eEcho says \"{echoResponse}\"\u003c/span\u003e,\n    });\n};\n```\n\nThe hook accepts a function that returns a `Promise`, as simple as that. The type parameter defines the type of data wrapped by the `Promise`. It returns an array with a `result` object that represents the result of the asynchronous operation and lets us match its states and a `load` function which simply calls the function provided as an argument within the hook.\n\nIt's also worth mentioning that matching the `Idle` state is optional - the mapping for the `Loading` state will be taken for the `Idle` state if none is passed.\n\n##### With arguments\n\nSometimes it is necessary to pass some arguments to the promise loading function. You can simply pass such function as an argument to he `usePromise` hook, it's type safe as the types of the arguments will be inferred in the returned loading function. It's also possible to explicitly define them in the second type argument of the hook.\n\n```tsx\nexport const UserEchoComponent = () =\u003e {\n    const [text, setText] = React.useState(\"Hello!\");\n    const echoWithArguments = (param: string) =\u003e echo(param);\n    const [result, load] = usePromise\u003cstring, [string]\u003e(echoWithArguments);\n    const onInputChange = (e: React.ChangeEvent\u003cHTMLInputElement\u003e) =\u003e setText(e.target.value);\n\n    const callEcho = React.useCallback(() =\u003e {\n        () =\u003e load(text);\n    }, [load]);\n\n    return (\n        \u003cdiv\u003e\n            \u003cinput value={text} onChange={onInputChange} /\u003e\n            \u003cbutton onClick={callEcho}\u003eCall echo!\u003c/button\u003e\n            {result.match({\n                Idle: () =\u003e \u003c\u003e\u003c/\u003e,\n                Loading: () =\u003e \u003cspan\u003eI say \"{text}\"!\u003c/span\u003e,\n                Rejected: (err) =\u003e \u003cspan\u003eOops, something went wrong! Error: {err}\u003c/span\u003e,\n                Resolved: (echoResponse) =\u003e \u003cspan\u003eEcho says \"{echoResponse}\"\u003c/span\u003e,\n            })}\n        \u003c/div\u003e\n    );\n};\n```\n\n##### Error handling\n\nWe can provide a third type parameter to the hook, which defines the type of error that is returned on rejection. By default, it is set to string. If we are using some type of domain exceptions in our services we could use the hook as following:\n\n```tsx\nconst [result] = usePromise\u003cSomeData, [], MyDomainException\u003e(() =\u003e myServiceMethod(someArgument));\n```\n\nand then we would match the `Rejected` state like that:\n\n```typescript\nresult.match({\n    //...\n    Rejected: (err: MyDomainException) =\u003e err.someCustomField,\n    //...\n});\n```\n\n#### Polling: usePromiseWithInterval\n\nIf you need to repeatedly poll the data (eg. by sending a request to the server), and do that on-demand, you can use `usePromiseWithInterval` hook. Pass the interval as a second argument, and receive the `result` and `start` \u0026 `stop` functions in return. `usePromiseInterval` uses `setTimeout` API for polling.\n\n```tsx\nexport const UserEchoWithIntervalComponent = () =\u003e {\n    const echoWithArguments = (param: string) =\u003e echo(param);\n    const [result, start, stop] = usePromiseWithInterval\u003cstring, [string]\u003e(echoWithArguments, 2000);\n\n    const startCallingEcho = React.useCallback(() =\u003e {\n        start(\"It's me again!!!\");\n    }, [start]);\n\n    return (\n        \u003c\u003e\n            {result.match({\n                Idle: () =\u003e \u003c\u003e\u003c/\u003e,\n                Loading: () =\u003e \u003cspan\u003eI say \"{text}\"!\u003c/span\u003e,\n                Rejected: (err) =\u003e \u003cspan\u003eOops, something went wrong! Error: {err}\u003c/span\u003e,\n                Resolved: (echoResponse) =\u003e \u003cspan\u003eEcho says \"{echoResponse}\"\u003c/span\u003e,\n            })}\n        \u003c/\u003e\n    );\n};\n```\n\nBesides that, you can also perform calls manually, and check the current amount of times your request was performed.\n\n```tsx\nexport const IntervalAndManualCheckComponent = () =\u003e {\n    const echoWithArguments = (param: string) =\u003e echo(param);\n    const [\n        result, // good old PromiseResultShape\n        start,\n        stop,\n        load, // manual load trigger\n        reset, // promise shape reset function\n        tryCount // amount of times your request was performed\n    ] = usePromiseWithInterval\u003cstring, [string]\u003e(echoWithArguments, 2000);\n\n    const startCallingEcho = React.useCallback(() =\u003e {\n        start(\"It's me again!!!\");\n    }, [start]);\n\n    return (\n        \u003c\u003e\n            {result.match({\n                Idle: () =\u003e \u003c\u003e\u003c/\u003e,\n                Loading: () =\u003e \u003cspan\u003eI say \"{text}\"!\u003c/span\u003e,\n                Rejected: (err) =\u003e \u003cspan\u003eOops, something went wrong! Error: {err}\u003c/span\u003e,\n                Resolved: (echoResponse) =\u003e \u003cspan\u003eEcho says \"{echoResponse}\"\u003c/span\u003e,\n            })}\n        \u003c/\u003e\n        \u003cbutton onClick={stop}\u003eStop\u003c/button\u003e\n        \u003cbutton disabled={retries \u003c 3} onClick={load}\u003eRetry manually\u003c/button\u003e\n    );\n};\n```\n\n### Callback functions\n\nApart from rendering phase, you may want to perform some side effect functions when your promise is in a specific state. I.e. you may want to invoke another asynchronous function when the data is resolved or when the error is being thrown.\n\nTo do that, you can use callback functions dedicated to every promise state.\n\n```tsx\nconst [result1, load1] = usePromise\u003cSomeData, [], MyDomainException\u003e(() =\u003e myServiceMethod(someArgument));\nconst [result2, load2] = usePromise\u003cSomeDataB, [Result1ResponseData], MyDomainException\u003e(anotherServiceMethod);\n\nresult1\n    .onIdle(() =\u003e console.log(\"Promise is idle\"))\n    .onLoading(() =\u003e console.log(\"Yaaay, bring the data on!\"))\n    .onResolved((response) =\u003e load2(response.data))\n    .onRejected((err) =\u003e console.log(err.response.data));\n\nReact.useEffect(() =\u003e {\n    // run this after the component mounts\n    load1();\n}, [load1]);\n```\n\nEvery callback function is chainable - `onIdle`, `onLoading`, `onResolved` and `onRejected` return the `PromiseResultShape` instance.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftwaremill%2Freact-use-promise-matcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoftwaremill%2Freact-use-promise-matcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoftwaremill%2Freact-use-promise-matcher/lists"}