{"id":20825905,"url":"https://github.com/upstatement/react-hooks","last_synced_at":"2025-05-07T20:36:22.572Z","repository":{"id":40288326,"uuid":"250333945","full_name":"Upstatement/react-hooks","owner":"Upstatement","description":"Repository of reusable custom React hooks","archived":false,"fork":false,"pushed_at":"2022-05-16T21:02:36.000Z","size":808,"stargazers_count":6,"open_issues_count":5,"forks_count":0,"subscribers_count":14,"default_branch":"master","last_synced_at":"2023-04-10T18:27:08.285Z","etag":null,"topics":["hooks","react","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@upstatement/react-hooks","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Upstatement.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-03-26T18:02:44.000Z","updated_at":"2023-02-17T03:43:29.000Z","dependencies_parsed_at":"2022-07-27T22:47:38.974Z","dependency_job_id":null,"html_url":"https://github.com/Upstatement/react-hooks","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Upstatement%2Freact-hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Upstatement%2Freact-hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Upstatement%2Freact-hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Upstatement%2Freact-hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Upstatement","download_url":"https://codeload.github.com/Upstatement/react-hooks/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225101896,"owners_count":17421082,"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":["hooks","react","typescript"],"created_at":"2024-11-17T23:07:04.431Z","updated_at":"2024-11-17T23:07:05.213Z","avatar_url":"https://github.com/Upstatement.png","language":"TypeScript","readme":"# @upstatement/react-hooks\n\n\u003e A collection of Upstatement's most-used React hooks\n\n![](https://github.com/Upstatement/react-hooks/workflows/CI/badge.svg?branch=master) [![](https://badgen.net/npm/v/@upstatement/react-hooks)](https://www.npmjs.com/package/@upstatement/react-hooks)\n\nA collection of Upstatement's most-used React hooks across projects, updated to have first-class TypeScript support!\n\n## Table of Contents\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Meet the Hooks](#-meet-the-hooks)\n- [Contributing](#contributing)\n- [Code of Conduct](#code-of-conduct)\n- [About Upstatement](#about-upstatement)\n\n## Requirements\n\nThis package has the following [peer dependencies](https://docs.npmjs.com/files/package.json#peerdependencies):\n\n- [React](https://www.npmjs.com/package/react) v16.8.0+ (for hooks ⚓️)\n\n## Installation\n\nWith [npm](https://www.npmjs.com/):\n\n```shell\n$ npm install @upstatement/react-hooks\n```\n\nWith [yarn](https://yarnpkg.com/):\n\n```shell\n$ yarn add @upstatement/react-hooks\n```\n\nThen with a module bundler like [webpack](https://webpack.github.io/), use as you would anything else:\n\n```js\n// using ES6 modules\nimport { useMultiRef, useSet } from '@upstatement/react-hooks';\nimport useMultiRef from '@upstatement/react-hooks/dist/esm/useMultiRef';\n\n// using CommonJS modules\nconst { useMultiRef, useSet } = require('@upstatement/react-hooks');\nconst useMultiRef = require('@upstatement/react-hooks/dist/cjs/useMultiRef');\n```\n\n## 👋 Meet the Hooks\n\n### `useState`\n\nThis basic hook provides an extra layer of security over React's existing `useState` hook. While it functions the exact same as the original, our hook will no longer accept updates to the state post-unmount.\n\n\u003cdetails\u003e\n\u003csummary\u003eLearn more about the \u003ccode\u003euseState\u003c/code\u003e API and Usage\u003c/summary\u003e\n\n#### API\n\nThe API remains unchanged from React's `useState` hook: https://reactjs.org/docs/hooks-reference.html#usestate\n\n#### Usage\n\n```jsx\nimport { useState } from '@upstatement/react-hooks';\n\nconst App = () =\u003e {\n  const [count, setCount] = useState(0);\n\n  const increment = () =\u003e {\n    setCount(count =\u003e count + 1);\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003ch4\u003eCount: {count}\u003c/h4\u003e\n      \u003cbutton onClick={increment}\u003eIncrement\u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n### `useStateReducer`\n\nThis hook is a step between the `useState` and `useReducer` hooks, providing a way to create a mini state store inside your component.\n\n\u003cdetails\u003e\n\u003csummary\u003eLearn more about the \u003ccode\u003euseStateReducer\u003c/code\u003e API and Usage\u003c/summary\u003e\n\n#### API\n\n```js\nconst [state, set] = useStateReducer(initialState);\n```\n\nThe initial state should be a record of key-value pairs. Similar to `useState`, these values can either be the exact value, or a function to be lazily evaluated when the hook is run. For example:\n\n```js\nconst [state, set] = useStateReducer({\n  name: 'John',\n  expensiveValue: () =\u003e {\n    const initialState = someExpensiveComputation();\n    return initialState;\n  },\n});\n```\n\nThe `set` function contains a number of properties that update the respective values within the state. For example:\n\n```js\nconst [state, set] = useStateReducer({\n  age: 6,\n  maxAge: 8,\n});\n\nset.age(5); // Sets age to 5\nset.age(age =\u003e age + 1); // Increases age by 1\nset.age((age, state) =\u003e Math.min(age + 1, state.maxAge)); // Increases age by 1, capped by current state's maxAge value\n```\n\n#### Usage\n\n```jsx\nimport { useStateReducer } from '@upstatement/react-hooks';\n\nconst UserForm = ({ onSubmit }) =\u003e {\n  const [state, set] = useStateReducer({\n    name: '',\n    age: 0,\n    createdAt: () =\u003e new Date(),\n  });\n\n  return (\n    \u003cform onSubmit={onSubmit}\u003e\n      \u003cinput id=\"name\" value={state.name} onChange={evt =\u003e set.name(evt.target.value)} /\u003e\n      \u003cinput id=\"age\" type=\"number\" value={state.age} onChange={evt =\u003e set.age(evt.target.value)} /\u003e\n    \u003c/form\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n### `usePrevious`\n\nThis hook allows for tracking of the value of a given variable on a previous render.\n\n\u003cdetails\u003e\n\u003csummary\u003eLearn more about the \u003ccode\u003eusePrevious\u003c/code\u003e API and Usage\u003c/summary\u003e\n\n#### API\n\n```js\nconst previousValue = usePrevious(currentValue);\n```\n\nThe initial previous value returned will be the same as the current value.\n\nIt's important to note that the previous value does not update when the given value changes, but rather on _every render_.\n\n#### Usage\n\n```jsx\nimport { usePrevious } from '@upstatement/react-hooks';\n\nconst Direction = ({ scrollY }) =\u003e {\n  const previousScrollY = usePrevious(scrollY);\n\n  if (scrollY === previousScrollY) {\n    return null;\n  } else if (scrollY \u003e previousScrollY) {\n    return \u003cArrowUp /\u003e;\n  }\n  return \u003cArrowDown /\u003e;\n};\n```\n\n\u003c/details\u003e\n\n### `useMap`\n\nThis hook allows for use of the [ES6 Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) as a state variable inside your React component.\n\n\u003cdetails\u003e\n\u003csummary\u003eLearn more about the \u003ccode\u003euseMap\u003c/code\u003e API and Usage\u003c/summary\u003e\n\n#### API\n\n```js\nconst map = useMap(arrayOfTuples);\n// Accepts the same initial value that Map's constructor does\n```\n\nAll map methods can then be used as normal, including (but not limited to) `map.set`, `map.has`, and `map.delete`.\n\n#### Usage\n\n```jsx\nimport { useMap, useState } from '@upstatement/react-hooks';\n\nconst DictionarySearch = () =\u003e {\n  const dictionaryMap = useMap();\n\n  const [search, setSearch] = useState('');\n  const [term, setTerm] = useState('');\n  const [definition, setDefinition] = useState('');\n\n  const addDefinition = evt =\u003e {\n    evt.preventDefault();\n    dictionaryMap.add(term, definition);\n    setTerm('');\n    setDefinition('');\n  };\n\n  const onChange = setFunction =\u003e evt =\u003e {\n    setFunction(evt.target.value);\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003cinput id=\"search\" value={search} onChange={onChange(setSearch)} /\u003e\n      {dictionaryMap.has(search) ? (\n        \u003cp style={{ color: 'green' }}\u003e{dictionaryMap.get(search)}\u003c/p\u003e\n      ) : (\n        \u003cp style={{ color: 'red' }}\u003eTerm not found in dictionary.\u003c/p\u003e\n      )}\n      \u003cform onSubmit={addDefinition}\u003e\n        \u003cinput id=\"term\" value={term} onChange={onChange(setTerm)} /\u003e\n        \u003ctextarea id=\"definition\" value={definition} onChange={onChange(setDefinition)}\u003e\u003c/textarea\u003e\n      \u003c/form\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n### `useSet`\n\nSimilar to `useMap`, this hook allows you to use an [ES6 Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) as a state variable inside your React component.\n\n\u003cdetails\u003e\n\u003csummary\u003eLearn more about the \u003ccode\u003euseSet\u003c/code\u003e API and Usage\u003c/summary\u003e\n\n#### API\n\n```js\nconst set = useSet(arrayOfValues);\n// Accepts the same initial value that Set's constructor does\n```\n\nAll set methods can then be used as normal, including (but not limited to) `set.add`, `set.has`, and `set.delete`.\n\n#### Usage\n\n```jsx\nimport { useSet } from '@upstatement/react-hooks';\n\nconst Shop = ({ items }) =\u003e {\n  const cartSet = useSet();\n\n  const addToCart = index =\u003e {\n    const item = items[index];\n    if (item) {\n      cartSet.add(item.name);\n    }\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003ch2\u003eItems\u003c/h2\u003e\n      \u003cul\u003e\n        {items.map(({ name, price }, index) =\u003e (\n          \u003cli key={name}\u003e\n            \u003cp\u003e{name}\u003c/p\u003e\n            \u003cp\u003e${price}\u003c/p\u003e\n            \u003cbutton disabled={cartSet.has(name)} onClick={() =\u003e addToCart(index)}\u003e\n              Add to cart\n            \u003c/button\u003e\n          \u003c/li\u003e\n        ))}\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n### `useMultiRef`\n\nAllows for tracking multiple refs in the React DOM. This is particularly useful when looping over items.\n\n\u003cdetails\u003e\n\u003csummary\u003eLearn more about the \u003ccode\u003euseMultiRef\u003c/code\u003e API and Usage\u003c/summary\u003e\n\n### API\n\n```js\nconst [refs, setRef] = useMultiRef();\n```\n\n### Usage\n\n```jsx\nimport { useEffect } from 'react';\nimport { useMultiRef } from '@upstatement/react-hooks';\nimport { last } from 'lodash';\n\nconst Modal = ({ links }) =\u003e {\n  const [linkRefs, setLinkRef] = useMultiRef();\n\n  const lockModalFocus = evt =\u003e {\n    if (evt.keyCode === 9) {\n      // Pressed tab\n      const linkEls = linkRefs.current;\n      if (evt.shiftKey \u0026\u0026 document.activeElement === linkEls[0]) {\n        evt.preventDefault();\n        last(linkEls).focus();\n      } else if (!evt.shiftKey \u0026\u0026 document.activeElement === last(linkEls)) {\n        evt.preventDefault();\n        linkEls[0].focus();\n      }\n    }\n  };\n\n  useEffect(() =\u003e {\n    linkRefs.current[0].focus();\n    window.addEventListener('keydown', lockModalFocus);\n    return () =\u003e {\n      window.removeEventListener('keydown', lockModalFocus);\n    };\n  }, []);\n\n  return (\n    \u003cul\u003e\n      {links.map(({ href, text }, index) =\u003e (\n        \u003cli key={index} ref={setLinkRef(index)}\u003e\n          \u003ca href={href}\u003e{text}\u003c/a\u003e\n        \u003c/li\u003e\n      ))}\n    \u003c/ul\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n### `useForceUpdate`\n\nThis utility hook provides a way to force the a component to update. It's recommended to _only be used_ when the DOM is dependent on a ref value.\n\n\u003cdetails\u003e\n\u003csummary\u003eLearn more about the \u003ccode\u003euseForceUpdate\u003c/code\u003e API and Usage\u003c/summary\u003e\n\n#### API\n\n```js\nconst update = useForceUpdate();\n```\n\nEvery call to the `update` function will increase an internal tick. This in turn will force a re-render of the component.\n\n#### Usage\n\nThis hook is actually used in our `useSet` and `useMap` hooks! A snippet of that code is found below:\n\n```js\nimport { useRef } from 'react';\nimport { useForceUpdate } from '@upstatement/react-hooks';\n\nconst useSet = iterable =\u003e {\n  const update = useForceUpdate();\n  const setRef = useRef(new Set(iterable));\n\n  const set = new Set(setRef.current);\n\n  set.add = value =\u003e {\n    const newSet = setRef.add(value); // Add to our set reference\n    update(); // force update to hook, recreating the `set` value\n    return newSet;\n  };\n\n  return set;\n};\n```\n\n\u003c/details\u003e\n\n## Contributing\n\nWe welcome all contributions to our projects! Filing bugs, feature requests, code changes, docs changes, or anything else you'd like to contribute are all more than welcome! More information about contributing can be found in the [contributing guidelines](.github/CONTRIBUTING.md).\n\n## Code of Conduct\n\nUpstatement strives to provide a welcoming, inclusive environment for all users. To hold ourselves accountable to that mission, we have a strictly-enforced [code of conduct](CODE_OF_CONDUCT.md).\n\n## About Upstatement\n\n[Upstatement](https://www.upstatement.com/) is a digital transformation studio headquartered in Boston, MA that imagines and builds exceptional digital experiences. Make sure to check out our [services](https://www.upstatement.com/services/), [work](https://www.upstatement.com/work/), and [open positions](https://www.upstatement.com/jobs/)!\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupstatement%2Freact-hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fupstatement%2Freact-hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupstatement%2Freact-hooks/lists"}