├── .babelrc.js ├── .gitignore ├── .huskyrc ├── .lintstagedrc ├── .npmrc ├── .prettierrc ├── LICENSE ├── README.md ├── package.json ├── src └── index.ts └── tsconfig.json /.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | loose: true, 7 | modules: false, 8 | }, 9 | ], 10 | '@babel/preset-typescript', 11 | ], 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | types 4 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,ts,json,md}": [ 3 | "prettier --write", 4 | "git add" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "all" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Andarist 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # use-constant 2 | 3 | React hook for creating a value exactly once. `useMemo` doesn't give this guarantee unfortunately - https://reactjs.org/docs/hooks-faq.html#how-to-create-expensive-objects-lazily 4 | 5 | ### Usage 6 | Install the package 7 | ```bash 8 | npm install use-constant 9 | # OR 10 | yarn add use-constant 11 | ``` 12 | 13 | In your code 14 | ```javascript 15 | import useConstant from 'use-constant'; 16 | 17 | const MyComponent = () => { 18 | // Give useConstant() a function which should be be executed exactly once and 19 | // return in it your constant value 20 | const myConstantValue = useConstant(() => 42); 21 | // ... 22 | ``` 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "use-constant", 3 | "version": "2.0.0", 4 | "description": "React hook for creating a value exactly once.", 5 | "main": "dist/use-constant.cjs.js", 6 | "module": "dist/use-constant.esm.js", 7 | "exports": { 8 | ".": { 9 | "types": { 10 | "import": "./dist/use-constant.cjs.mjs", 11 | "default": "./dist/use-constant.cjs.js" 12 | }, 13 | "module": "./dist/use-constant.esm.js", 14 | "import": "./dist/use-constant.cjs.mjs", 15 | "default": "./dist/use-constant.cjs.js" 16 | }, 17 | "./package.json": "./package.json" 18 | }, 19 | "types": "./dist/use-constant.cjs.d.ts", 20 | "files": [ 21 | "dist", 22 | "types" 23 | ], 24 | "scripts": { 25 | "test": "echo \"Warning: no test specified\" || jest --env=node", 26 | "build": "preconstruct build", 27 | "preversion": "npm test", 28 | "prepare": "npm run build" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/Andarist/use-constant.git" 33 | }, 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/Andarist/use-constant/issues" 37 | }, 38 | "homepage": "https://github.com/Andarist/use-constant#readme", 39 | "peerDependencies": { 40 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 41 | }, 42 | "devDependencies": { 43 | "@babel/core": "^7.26.10", 44 | "@babel/preset-env": "^7.26.9", 45 | "@babel/preset-typescript": "^7.27.0", 46 | "@preconstruct/cli": "^2.8.12", 47 | "@types/react": "^16.8.8", 48 | "fs-extra": "^7.0.1", 49 | "husky": "^1.3.1", 50 | "jest": "^24.5.0", 51 | "lint-staged": "^8.1.5", 52 | "prettier": "^1.16.4", 53 | "react": "^16.8.4", 54 | "typescript": "^5.8.3" 55 | }, 56 | "preconstruct": { 57 | "exports": { 58 | "importConditionDefaultExport": "default" 59 | }, 60 | "___experimentalFlags_WILL_CHANGE_IN_PATCH": { 61 | "importsConditions": true 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | type ResultBox = { v: T } 4 | 5 | export default function useConstant(fn: () => T): T { 6 | const ref = React.useRef>() 7 | 8 | if (!ref.current) { 9 | ref.current = { v: fn() } 10 | } 11 | 12 | return ref.current.v 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "declarationDir": "./types", 5 | "emitDeclarationOnly": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "lib": ["esnext"], 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "resolveJsonModule": true, 11 | "rootDir": "./src", 12 | "skipLibCheck": true, 13 | "strict": true, 14 | "target": "esnext" 15 | } 16 | } 17 | --------------------------------------------------------------------------------