The library provides a React hook with ability to automatically cancel asynchronous code inside it. It just makes it easier to write cancelable asynchronous code that doesn't cause the following React issue when unmounting, if your asynchronous tasks that change state are pending:
Warning: Can't perform a React state update on an unmounted component.
This is an no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous task in "a useEffect cleanup function".
It uses c-promise2 to make it work.
When it used in conjunction with other libraries that work with the CPromise,
such as cp-fetch and cp-axios,
you get a powerful tool for building asynchronous logic for your React components.
You just have to use generators
instead of an async function to make your code cancellable,
but basically, that just means you will have to use yield
instead of await
keyword.
- Install for node.js using npm/yarn:
$ npm install use-async-effect2 c-promise2
$ yarn add use-async-effect2 c-promise2
Minimal example with json request Live demo
import React from "react";
import {useState} from "react";
import {useAsyncEffect} from "use-async-effect2";
import cpFetch from "cp-fetch";
function JSONViewer(props) {
const [text, setText] = useState("");
useAsyncEffect(function* () {
setText("fetching...");
const response = yield cpFetch(props.url);
const json = yield response.json();
setText(`Success: ${JSON.stringify(json)}`);
}, [props.url]);
return <div>{text}</div>;
}
Example with a timeout & error handling (Live demo):
import React from "react";
import {useState} from "react";
import {useAsyncEffect, E_REASON_UNMOUNTED} from "use-async-effect2";
import {CanceledError} from "c-promise2";
import cpFetch from "cp-fetch";
export default function TestComponent(props) {
const [text, setText] = useState("");
const [cancel]= useAsyncEffect(function* ({onCancel}) {
console.log("mount");
this.timeout(5000);
onCancel(()=> console.log('scope canceled'));
try {
setText("fetching...");
const response = yield cpFetch(props.url);
const json = yield response.json();
setText(`Success: ${JSON.stringify(json)}`);
} catch (err) {
CanceledError.rethrow(err, E_REASON_UNMOUNTED); //passthrough
setText(`Failed: ${err}`);
}
return () => {
console.log("unmount", this.isCanceled);
};
}, [props.url]);
//setTimeout(()=> cancel("Ooops!"), 1000);
return <div>{text}</div>;
}
useAsyncCallback example (Live demo):
import React from "react";
import {useState} from "react";
import {useAsyncCallback} from "use-async-effect2";
import {CPromise} from "c-promise2";
export default function TestComponent(props) {
const [text, setText] = useState("");
const asyncRoutine= useAsyncCallback(function*(v){
setText(`Stage1`);
yield CPromise.delay(1000);
setText(`Stage2`);
yield CPromise.delay(1000);
setText(`Stage3`);
yield CPromise.delay(1000);
setText(`Done`);
return v;
})
const onClick= ()=>{
asyncRoutine(123).then(value=>{
console.log(`Result: ${value}`)
}, console.warn);
}
return <div><button onClick={onClick}>Run async job</button><div>{text}</div></div>;
}
To learn more about available features, see the c-promise2 documentation.
To get it, clone the repository and run npm run playground
in the project directory or
just use the codesandbox demo to play with the library online.
A React hook based on useEffect
, that resolves passed generator as asynchronous function.
The asynchronous generator sequence and its promise of the result will be canceled if
the effect cleanup process is started before it completes.
The generator can return a cleanup function similar to the useEffect
hook.
generatorFn(...userArgs, scope: CPromise)
:GeneratorFunction
- generator to resolve as an async function. The last argument passed to this function andthis
refer to the CPromise instance.deps?: any[]
- effect dependencies
This hook makes an async callback that can be automatically canceled on unmount or by user request.
deps: any[]
combine:boolean
- allow only single thread running. All subsequent callings will return promises that subscribed to the pending promise of the first call.cancelPrevious:boolean
- cancel the previous pending async function before running a new one.concurrency: number=0
- set concurrency limit for simultaneous calls.
- c-promise2 - promise with cancellation, decorators, timeouts, progress capturing, pause and user signals support
- cp-axios - a simple axios wrapper that provides an advanced cancellation api
- cp-fetch - fetch with timeouts and request cancellation
The MIT License Copyright (c) 2021 Dmitriy Mozgovoy robotshara@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.