{"id":16400638,"url":"https://github.com/dilan-dio4/use-safe-async-mount","last_synced_at":"2025-03-21T02:32:56.384Z","repository":{"id":63324552,"uuid":"564949962","full_name":"dilan-dio4/use-safe-async-mount","owner":"dilan-dio4","description":"React \u0026 React Native hook for mounting asynchronous components with type-safe definitions.","archived":false,"fork":false,"pushed_at":"2024-08-27T01:22:52.000Z","size":63,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-10-12T05:28:11.661Z","etag":null,"topics":["expo","express","hooks","nextjs","nodejs","npm","pnpm","react","react-hooks","react-native","reactjs","typescript","vite","vscode","webpack"],"latest_commit_sha":null,"homepage":"https://dilan-dio4.github.io/use-safe-async-mount/test/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dilan-dio4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-11-11T22:26:51.000Z","updated_at":"2024-09-12T02:00:20.000Z","dependencies_parsed_at":"2022-11-17T02:00:29.183Z","dependency_job_id":null,"html_url":"https://github.com/dilan-dio4/use-safe-async-mount","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dilan-dio4%2Fuse-safe-async-mount","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dilan-dio4%2Fuse-safe-async-mount/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dilan-dio4%2Fuse-safe-async-mount/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dilan-dio4%2Fuse-safe-async-mount/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dilan-dio4","download_url":"https://codeload.github.com/dilan-dio4/use-safe-async-mount/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221811380,"owners_count":16884305,"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":["expo","express","hooks","nextjs","nodejs","npm","pnpm","react","react-hooks","react-native","reactjs","typescript","vite","vscode","webpack"],"created_at":"2024-10-11T05:28:13.716Z","updated_at":"2024-10-28T09:12:05.227Z","avatar_url":"https://github.com/dilan-dio4.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- markdownlint-disable MD033 MD041 --\u003e\n\n\u003cbr /\u003e\n\n\u003ch1 align=\"center\"\u003e\n\nuse-safe-async-mount\u003cbr\u003e\u003csub\u003eThe Asynchronous Functional Component Mounter\u003c/sub\u003e\n\n\u003c/h1\u003e\n\n\u003cbr /\u003e\n\n## The Problem\n\nThis is a `useEffect` hook with zero dependencies:\n\n```js\nuseEffect(() =\u003e {\n  // Code that runs \"on mount\" (unfortunately)\n}, [])\n```\n\nIt is the most incorrectly [_and dangerously_] used part of the react functional component lifecycle.\n\nWhenever using this hook (_with the empty dependency array_), you should ask yourself two questions:\n\n1. Am I using this to **just** initialize a variable based on some synchronously computed value?\n    * ```useEffect(() =\u003e { setValue(computeMyValue()) }, [])```\n\n2. Am I using this to **just** conditionally initialize a state variable based on props?\n    * ```useEffect(() =\u003e { setValue(someProp ? \"a\" : \"b\") }, [])```\n\nIf 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_.\n\nHere's some situations that **do** fit in the empty `useEffect` hook:\n\n* Making a network request\n* Setting/removing an event listener\n* UI-related analytics tracking\n\n## Async State\n\nWhen 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:\n\n### The Traditional Pattern\n\n```js\nfunction ExampleComponent() {\n  const [stateOne, setStateOne] = useState()\n\n  useEffect(() =\u003e {\n    someAsyncFunction().then(res =\u003e setStateOne(res))\n    // OR\n    const run = async () =\u003e {\n      const res = await someAsyncFunction()\n      setStateOne(res)\n    }\n    run();\n  }, [])\n\n  return (\n    \u003c\u003e\n        \u003ch1\u003eMy Example Component\u003c/h1\u003e\n        {stateOne \u0026\u0026 \u003cp\u003e{stateOne}\u003c/p\u003e}\n    \u003c/\u003e\n  )\n}\n```\n\n## The Drawbacks\n\nThere's three negative implications to this solution:\n\n1. When the component initially mounts, the variable is _undefined_. This requires more render logic.\n2. 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**.\n3. In TypeScript projects, your compiler won't recognize that your variable has been defined. That means `!`'s everywhere.\n\n## The Solution\n\n### Installation\n\n```bash\nnpm i use-safe-async-mount\n```\n\n`use-safe-async-mount` solves these problems by acting as a **true hook-based, type-safe `componentWillMount`** implementation.\n\n### Example\n\n```js\nimport useSafeAsyncMount from 'use-safe-async-mount';\n\nfunction ExampleComponent() {\n\n  const { SafeRender } = useSafeAsyncMount(async isActive =\u003e {\n    const res = await someAsyncFunction()\n    if (isActive()) {\n      // ^ This avoids setting component state after unmount\n      \n      // These values are defined and type-safe in the `SafeRender` component\n      return { \n        stateOne: \"Some value my component depends on\",\n        stateTwo: res\n      }\n    }\n  })\n\n  return (\n    \u003c\u003e\n      \u003ch1\u003eMy Example Component\u003c/h1\u003e\n      \u003cSafeRender\u003e\n        {({ stateOne, stateTwo }) =\u003e (\n          \u003cp\u003e{stateOne}\u003c/p\u003e\n        )}\n      \u003c/SafeRender\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n## Example\n\nHere's an [interactive example](https://dilan-dio4.github.io/use-safe-async-mount/test/) and the [associated source code](/test/src/App.tsx).\n\n## Inspirations\n\n* [use-async-effect](https://github.com/rauldeheer/use-async-effect)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdilan-dio4%2Fuse-safe-async-mount","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdilan-dio4%2Fuse-safe-async-mount","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdilan-dio4%2Fuse-safe-async-mount/lists"}