Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dilan-dio4/use-safe-async-mount
React & React Native hook for mounting asynchronous components with type-safe definitions.
https://github.com/dilan-dio4/use-safe-async-mount
expo express hooks nextjs nodejs npm pnpm react react-hooks react-native reactjs typescript vite vscode webpack
Last synced: 2 months ago
JSON representation
React & React Native hook for mounting asynchronous components with type-safe definitions.
- Host: GitHub
- URL: https://github.com/dilan-dio4/use-safe-async-mount
- Owner: dilan-dio4
- Created: 2022-11-11T22:26:51.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-08-27T01:22:52.000Z (5 months ago)
- Last Synced: 2024-10-12T05:28:11.661Z (3 months ago)
- Topics: expo, express, hooks, nextjs, nodejs, npm, pnpm, react, react-hooks, react-native, reactjs, typescript, vite, vscode, webpack
- Language: TypeScript
- Homepage: https://dilan-dio4.github.io/use-safe-async-mount/test/
- Size: 61.5 KB
- Stars: 7
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
use-safe-async-mount
The Asynchronous Functional Component Mounter
## The Problem
This is a `useEffect` hook with zero dependencies:
```js
useEffect(() => {
// Code that runs "on mount" (unfortunately)
}, [])
```It is the most incorrectly [_and dangerously_] used part of the react functional component lifecycle.
Whenever using this hook (_with the empty dependency array_), you should ask yourself two questions:
1. Am I using this to **just** initialize a variable based on some synchronously computed value?
* ```useEffect(() => { setValue(computeMyValue()) }, [])```2. Am I using this to **just** conditionally initialize a state variable based on props?
* ```useEffect(() => { setValue(someProp ? "a" : "b") }, [])```If either of these situations describes you, there's a high chance you should be just be computing the value in-line or using `useMemo`. This **saves** you from having to deal with the initial render when your value (`useState`, `useRef`, `var`, etc.) is _undefined_.
Here's some situations that **do** fit in the empty `useEffect` hook:
* Making a network request
* Setting/removing an event listener
* UI-related analytics tracking## Async State
When a component's state depends on a value gathered from an async function, the common solution is to manually invoke it directly from the empty `useEffect` hook:
### The Traditional Pattern
```js
function ExampleComponent() {
const [stateOne, setStateOne] = useState()useEffect(() => {
someAsyncFunction().then(res => setStateOne(res))
// OR
const run = async () => {
const res = await someAsyncFunction()
setStateOne(res)
}
run();
}, [])return (
<>
My Example Component
{stateOne &&{stateOne}
}
>
)
}
```## The Drawbacks
There's three negative implications to this solution:
1. When the component initially mounts, the variable is _undefined_. This requires more render logic.
2. The component may unmount during the asynchronous request. Setting a state variable on an unmounted component is a **memory leak and will throw an error**.
3. In TypeScript projects, your compiler won't recognize that your variable has been defined. That means `!`'s everywhere.## The Solution
### Installation
```bash
npm i use-safe-async-mount
````use-safe-async-mount` solves these problems by acting as a **true hook-based, type-safe `componentWillMount`** implementation.
### Example
```js
import useSafeAsyncMount from 'use-safe-async-mount';function ExampleComponent() {
const { SafeRender } = useSafeAsyncMount(async isActive => {
const res = await someAsyncFunction()
if (isActive()) {
// ^ This avoids setting component state after unmount
// These values are defined and type-safe in the `SafeRender` component
return {
stateOne: "Some value my component depends on",
stateTwo: res
}
}
})return (
<>
My Example Component
{({ stateOne, stateTwo }) => (
{stateOne}
)}
>
)
}
```## Example
Here's an [interactive example](https://dilan-dio4.github.io/use-safe-async-mount/test/) and the [associated source code](/test/src/App.tsx).
## Inspirations
* [use-async-effect](https://github.com/rauldeheer/use-async-effect)