{"id":13828956,"url":"https://github.com/andywer/use-inline-memo","last_synced_at":"2025-05-07T16:08:54.936Z","repository":{"id":38419263,"uuid":"201880272","full_name":"andywer/use-inline-memo","owner":"andywer","description":"⚛️ React hook for memoizing values inline anywhere in a component","archived":false,"fork":false,"pushed_at":"2024-06-19T19:39:56.000Z","size":281,"stargazers_count":166,"open_issues_count":4,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-07T16:08:21.961Z","etag":null,"topics":["memoization","react","react-hooks","reactjs"],"latest_commit_sha":null,"homepage":"","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/andywer.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-12T07:30:03.000Z","updated_at":"2025-02-19T10:53:19.000Z","dependencies_parsed_at":"2024-06-20T08:03:38.943Z","dependency_job_id":null,"html_url":"https://github.com/andywer/use-inline-memo","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andywer%2Fuse-inline-memo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andywer%2Fuse-inline-memo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andywer%2Fuse-inline-memo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andywer%2Fuse-inline-memo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andywer","download_url":"https://codeload.github.com/andywer/use-inline-memo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252912996,"owners_count":21824066,"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":["memoization","react","react-hooks","reactjs"],"created_at":"2024-08-04T09:03:22.507Z","updated_at":"2025-05-07T16:08:54.910Z","avatar_url":"https://github.com/andywer.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e⚛︎ use-inline-memo\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003eReact hook for memoizing values and callbacks anywhere in a component.\u003c/b\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://travis-ci.org/andywer/use-inline-memo\"\u003e\u003cimg alt=\"Build status\" src=\"https://travis-ci.org/andywer/use-inline-memo.svg?branch=master\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/use-inline-memo\"\u003e\u003cimg alt=\"npm version\" src=\"https://img.shields.io/npm/v/use-inline-memo.svg\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\nLike other hooks, you can call [`React.useMemo()`](https://reactjs.org/docs/hooks-reference.html#usememo) and [`React.useCallback()`](https://reactjs.org/docs/hooks-reference.html#usecallback) only at the top of your component function and not use them conditionally.\n\nInline memos let us memoize anywhere without the restrictions that apply to the usage of hooks!\n\n```jsx\nimport { Button, TextField } from \"@material-ui/core\"\nimport React from \"react\"\nimport useInlineMemo from \"use-inline-memo\"\n\nfunction NameForm(props) {\n  const memo = useInlineMemo()\n  const [newName, setNewName] = React.useState(props.prevName)\n\n  // Conditional return prevents calling any hook after this line\n  if (props.disabled) {\n    return \u003cdiv\u003e(Disabled)\u003c/div\u003e\n  }\n\n  return (\n    \u003cReact.Fragment\u003e\n      \u003cTextField\n        label=\"Name\"\n        onChange={memo.nameChange(event =\u003e setNewName(event.target.value), [])}\n        value={newName}\n      /\u003e\n      \u003cButton\n        onClick={memo.submitClick(() =\u003e props.onSubmit(newName), [newName])}\n        style={memo.submitStyle({ margin: \"16px auto 0\" }, [])}\n      \u003e\n        Submit\n      \u003c/Button\u003e\n    \u003c/React.Fragment\u003e\n  )\n}\n```\n\n## Installation\n\n```\nnpm install use-inline-memo\n```\n\n## Usage\n\nEverytime you want to memoize a value, call `memo.X()` with an identifier `X` of your choice. This identifier will be used to map the current call to values memoized in previous component renderings.\n\nCalling `useInlineMemo()` without arguments should work in all ES2015+ runtimes as it requires the ES2015 `Proxy`. If you need to support older browsers like the Internet Explorer, you will have to state the memoization keys up-front:\n\n```jsx\nfunction NameForm(props) {\n  // Explicit keys to make it work in IE11\n  const memo = useInlineMemo(\"nameChange\", \"submitClick\", \"submitStyle\")\n  const [newName, setNewName] = React.useState(props.prevName)\n\n  return (\n    \u003cReact.Fragment\u003e\n      \u003cTextField\n        label=\"Name\"\n        onChange={memo.nameChange(event =\u003e setNewName(event.target.value), [])}\n        value={newName}\n      /\u003e\n      \u003cButton\n        onClick={memo.submitClick(() =\u003e props.onSubmit(newName), [newName])}\n        style={memo.submitStyle({ margin: \"16px auto 0\" }, [])}\n      \u003e\n        Submit\n      \u003c/Button\u003e\n    \u003c/React.Fragment\u003e\n  )\n}\n```\n\nWhen not run in production, there is also a check in place to prevent you from accidentally using the same identifier for two different values. Calling `memo.X()` with the same `X` twice during one rendering will lead to an error.\n\n## Use cases\n\n### Event listeners\n\nInline memoizers are perfect to define simple one-line callbacks in-place in the JSX without sacrificing performance.\n\n```jsx\n// Before\nfunction Component() {\n  const [value, setValue] = React.useState(\"\")\n  const onUserInput = React.useCallback(\n    (event: React.SyntheticEvent) =\u003e setValue(event.target.value),\n    []\n  )\n  return (\n    \u003cinput onChange={onUserInput} value={value} /\u003e\n  )\n}\n```\n\n```jsx\n// After\nfunction Component() {\n  const memo = useInlineMemo()\n  const [value, setValue] = React.useState(\"\")\n  return (\n    \u003cinput\n      onChange={memo.textChange(event =\u003e setValue(event.target.value), [])}\n      value={value}\n    /\u003e\n  )\n}\n```\n\n### `style` props \u0026 other objects\n\nUsing inline style props is oftentimes an express ticket to unnecessary re-renderings as you will create a new style object on each rendering, even though its content will in many cases never change.\n\n```jsx\n// Before\nfunction Component() {\n  return (\n    \u003cButton style={{ color: \"red\" }} type=\"submit\"\u003e\n      Delete\n    \u003c/Button\u003e\n  )\n}\n```\n\n```jsx\n// After\nfunction Component() {\n  const memo = useInlineMemo()\n  return (\n    \u003cButton style={memo.buttonStyle({ color: \"red\" }, [])} type=\"submit\"\u003e\n      Delete\n    \u003c/Button\u003e\n  )\n}\n```\n\nYou don't need to memoize every style object of every single DOM element, though. Use it whenever you pass an object to a complex component which is expensive to re-render.\n\nFor more background information check out [FAQs: Why memoize objects?](#faqs).\n\n## API\n\n#### `useInlineMemo(): MemoFunction`\n\nCall it once in a component to obtain the `memo` object carrying the memoization functions.\n\n#### `useInlineMemo(...keys: string[]): MemoFunction`\n\nState the memoization keys explicitly if you need to support Internet Explorer where `Proxy` is not available.\n\n#### `memo[id: string](value: T, deps: any[]): T`\n\nWorks the same as a call to `React.useMemo()` or `React.useCallback()`, only without the wrapping callback function. That wrapping function is useful to run expensive instantiations only if we actually refresh the value, but for our use case this is rather unlikely, so we provide you with a more convenient API instead.\n\n`id` is used to map different memoization calls between component renderings.\n\n## FAQs\n\n\u003cdetails\u003e\n\u003csummary\u003eHow does that work?\u003c/summary\u003e\n\nThe reason why React hooks cannot be called arbitrarily is that React needs to match the current hook call to previous calls. The only way it can match them is by assuming that the same hooks will always be called in the same order.\n\nSo what we do here is to provide a hook `useInlineMemo()` that creates a `Map` to match `memo.X()` calls to the memoized value and the deps array. We can match calls to `memo.X()` between different re-renderings by using `X` as an identifier.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWhy is memoization so important?\u003c/summary\u003e\n\nTo ensure good performance you want to re-render as few components as possible if some application state changes. In React we use `React.memo()` for that which judges by comparing the current component props to the props of the previous rendering.\n\nWithout memoization we will very often create the same objects, callbacks, ... with the same content over and over again for each rendering, but as they are new instances every time, they will not be recognized as the same values and cause unnecessary re-renderings (see \"Why memoize objects?\" below).\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWhy memoize objects?\u003c/summary\u003e\n\nWhen `React.memo()` determines whether a component needs to be re-rendered, it tests if the property values are equivalent to the property values of the last rendering. It does though by comparing them for equality by reference, as anything else would take too much time.\n\nNow if the parent component creates a new object and passes it to the component as a property, React will only check if the object is exactly the same object instance as for the last rendering (`newObject === prevObject`). Even if the object has exactly the same properties as before, with the exact same values, it will nevertheless be a new object that just happens to have the same content.\n\nWithout memoization `React.memo()` will always re-render the component as we keep passing new object instances – the equality comparison of the old and the new property value will never be true. Memoization makes sure to re-use the actual last object instance, thus skipping re-rendering.\n\u003c/details\u003e\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandywer%2Fuse-inline-memo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandywer%2Fuse-inline-memo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandywer%2Fuse-inline-memo/lists"}