[
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\ndist\n"
  },
  {
    "path": "README.md",
    "content": "# useDelayedRender ![npm bundle size](https://img.shields.io/bundlephobia/minzip/use-delayed-render)\n\nuseDelayedRender is a react hook for delaying the render and unmount of a component. This is commonly used to animate UI on unmount.\n\n<br />\n\n## Installation\n\n```\n$ yarn add use-delayed-render\n```\n\n<br />\n\n## Usage\n\nFunction signature:\n\n```ts\nconst { mounted: boolean, rendered: boolean } = useDelayedRender(\n  active: boolean,\n  options?: {\n    enterDelay: number,\n    exitDelay: number,\n    onUnmount: () => void\n  }\n)\n```\n\nOptions:\n\n- `active`: Whether your component is in an active state\n- `enterDelay`: After mounting, the delay before `rendered` becomes true\n- `exitDelay`: After `rendered` becomes false, the delay before unmounting\n- `onUnmount`: A callback triggered after unmounting\n\nReturn values:\n\n- `mounted`: Whether your component should be mounted in the DOM\n- `rendered`: Whether your component should be visible\n\n<br />\n\n## Example\n\nRender a modal, but delay the unmount so that our 2 second CSS transition completes before the modal is removed from the DOM.\n\n```js\nconst Modal = ({ active }) => {\n  const { mounted, rendered } = useDelayedRender(active, {\n    exitDelay: 2000,\n  })\n\n  if (!mounted) return null\n\n  return (\n    <Portal>\n      <div className={rendered ? 'modal visible' : 'modal'}>{/* ... */}</div>\n    </Portal>\n  )\n}\n```\n\nThis allows you to use simple CSS transitions to animate the mounting/unmounting of your component.\n\n```css\n.modal {\n  opacity: 0;\n  transition: opacity 2s ease;\n}\n\n.modal.visible {\n  opacity: 1;\n}\n```\n\n<br />\n\n## Why?\n\n- Usually you would use [`react-transition-group`](https://github.com/reactjs/react-transition-group) to solve this, but the 2.37MB install size is a bit overkill, compared to this package at 491B gzipped.\n\n```jsx\n<Transition in={active} unmountOnExit timeout={200} onExited={handleExit}>\n  <Modal />\n</Transition>\n```\n\n- Hooks solve the problem without needing a render function or HOC.\n"
  },
  {
    "path": "index.ts",
    "content": "import { useState, useRef, useCallback } from 'react'\n\ninterface Options {\n  enterDelay?: number\n  exitDelay?: number\n  onUnmount?: () => void\n}\n\nconst useDelayedRender = (active: boolean = false, options: Options = {}) => {\n  const [, force] = useState<any>()\n  const mounted = useRef(active)\n  const rendered = useRef(false)\n  const renderTimer = useRef<NodeJS.Timeout | null>(null)\n  const unmountTimer = useRef<NodeJS.Timeout | null>(null)\n  const prevActive = useRef(active)\n\n  const recalculate = useCallback(() => {\n    const { enterDelay = 1, exitDelay = 0 } = options\n\n    if (prevActive.current) {\n      // Mount immediately\n      mounted.current = true\n      if (unmountTimer.current) clearTimeout(unmountTimer.current)\n\n      if (enterDelay <= 0) {\n        // Render immediately\n        rendered.current = true\n      } else {\n        if (renderTimer.current) return\n\n        // Render after a delay\n        renderTimer.current = setTimeout(() => {\n          rendered.current = true\n          renderTimer.current = null\n          force({})\n        }, enterDelay)\n      }\n    } else {\n      // Immediately set to unrendered\n      rendered.current = false\n\n      if (exitDelay <= 0) {\n        mounted.current = false\n      } else {\n        if (unmountTimer.current) return\n\n        // Unmount after a delay\n        unmountTimer.current = setTimeout(() => {\n          mounted.current = false\n          unmountTimer.current = null\n          force({})\n        }, exitDelay)\n      }\n    }\n  }, [options])\n\n  // When the active prop changes, need to re-calculate\n  if (active !== prevActive.current) {\n    prevActive.current = active\n    // We want to do this synchronously with the render, not in an effect\n    // this way when active → true, mounted → true in the same pass\n    recalculate()\n  }\n\n  return {\n    mounted: mounted.current,\n    rendered: rendered.current\n  }\n}\n\nexport default useDelayedRender\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"use-delayed-render\",\n  \"version\": \"0.1.0-beta.0\",\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"module\": \"dist/index.modern.js\",\n  \"source\": \"index.ts\",\n  \"license\": \"MIT\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"scripts\": {\n    \"prepublish\": \"yarn build\",\n    \"build\": \"microbundle --compress --no-sourcemap\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^16.9.35\",\n    \"microbundle\": \"^0.13.3\",\n    \"typescript\": \"^3.8.3\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"*\"\n  },\n  \"dependencies\": {},\n  \"author\": \"@pacocoursey\",\n  \"repository\": \"pacocoursey/use-delayed-render\"\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"allowJs\": true,\n    \"jsx\": \"preserve\",\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"lib\": [\"dom\", \"es2019\"],\n    \"noEmit\": true,\n    \"moduleResolution\": \"node\",\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"noImplicitAny\": true,\n    \"strictNullChecks\": true,\n    \"strictFunctionTypes\": true,\n    \"strictBindCallApply\": true,\n    \"strictPropertyInitialization\": true,\n    \"noImplicitThis\": true,\n    \"alwaysStrict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"esModuleInterop\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true\n  }\n}\n"
  }
]