├── .gitignore
├── .npmignore
├── .prettierrc
├── LICENSE
├── README.md
├── demo
├── RendersCount.tsx
├── index.html
├── index.tsx
└── style.css
├── package.json
├── src
└── index.ts
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 | .env.test
60 |
61 | # parcel-bundler cache (https://parceljs.org/)
62 | .cache
63 |
64 | # next.js build output
65 | .next
66 |
67 | # nuxt.js build output
68 | .nuxt
69 |
70 | # vuepress build output
71 | .vuepress/dist
72 |
73 | # Serverless directories
74 | .serverless/
75 |
76 | # FuseBox cache
77 | .fusebox/
78 |
79 | # DynamoDB Local files
80 | .dynamodb/
81 |
82 | dist
83 |
84 | demoBuild
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | .cache
3 | demo
4 |
5 | #tests
6 | test
7 | coverage
8 |
9 | #build tools
10 | .travis.yml
11 | .jenkins.yml
12 | .codeclimate.yml
13 |
14 | #linters
15 | .jscsrc
16 | .jshintrc
17 | .eslintrc*
18 |
19 | #editor settings
20 | .idea
21 | .editorconfig
22 |
23 | yarn.lock
24 | tsconfig.json
25 | .prettierrc
26 | .gitignore
27 | demoBuild
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "semi": true,
4 | "singleQuote": true,
5 | "trailingComma": "all",
6 | "bracketSpacing": true,
7 | "jsxBracketSameLine": false,
8 | "arrowParens": "always"
9 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Adam Pietrasiak
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 | # Useage
2 |
3 | `yarn add use-method`
4 |
5 | ```ts
6 | import { useMethod } from 'use-method';
7 |
8 | function MyComponent() {
9 | const randomNumber = Math.random();
10 |
11 | // returns function that keeps the same reference during entire lifecycle of the component, while always using 'fresh' variables from last render
12 | const hiMethod = useMethod((name) => {
13 | // it'll always have `randomNumber` variable from the last render
14 | alert(`Hi, ${name} (${randomNumber})!`);
15 | });
16 |
17 | // hiMethod reference will remain the same on every render - if you'll pass it to other components like it'll never re-render if it's memo or PureComponent
18 | }
19 | ```
20 |
21 | # Introduction & Rationale
22 |
23 | When hooks got introduced - it's became problematic to keep functions references the same on each render.
24 |
25 | With classes it's not a problem. You just call `this.handleClick` which has the same reference on every render.
26 |
27 | With functional components, you define such functions during rendering like
28 |
29 | ```tsx
30 | function SomeComponent() {
31 | function handleClick() {
32 | // handling click
33 | }
34 |
35 | // rendering
36 | }
37 | ```
38 |
39 | Without any optimization - `handleClick` would result in a brand new function on every render.
40 |
41 | React `useCallback` hook is created to help solving this problem.
42 |
43 | But even `useCallback` updates function reference when variables used as dependencies change.
44 |
45 | Also - useCallback dependencies list tend to 'pollute' the code and might make it easy to introduce nasty bugs if you don't use proper linter.
46 |
47 | `use-method` makes provided function behave like class method. It will have the same reference on every render, but it'll use values from last render.
48 |
49 | Let's consider such example:
50 |
51 | ```tsx
52 | function PeopleToggler({ people, onToggle }) {
53 | const togglePerson = useMethod((person) => {
54 | // we want to always use `fresh` people and onToggle props here
55 | const peopleAfterToggle = oggleInArray(people, person);
56 | onToggle(peopleAfterToggle);
57 | });
58 |
59 | return ;
60 | }
61 | ```
62 |
63 | When `togglePerson` is called - it has access to fresh `people` and `onToggle` values.
64 |
65 | At the same time, `togglePerson` reference remains constant.
66 |
67 | The same component using `useCallback` could look like:
68 |
69 | ```tsx
70 | function PeopleToggler({ people, onToggle }) {
71 | const togglePerson = useCallback(
72 | (person) => {
73 | // we need fresh `people` and `onToggle` here
74 | const peopleAfterToggle = oggleInArray(people, person);
75 | onToggle(peopleAfterToggle);
76 | },
77 | [people, onToggle],
78 | );
79 |
80 | return ;
81 | }
82 | ```
83 |
84 | In this case, `togglePerson` will actually get a new reference every time props `people` or `onToggle` will update.
85 |
86 | ## When to use it and when not to use it?
87 |
88 | You can think about it in the same way as class method.
89 |
90 | In class, if you'd have something like
91 |
92 | ```tsx
93 |
94 | ```
95 |
96 | It's fine, but if you'd have something like
97 |
98 | ```tsx
99 | class Table extends Component {
100 | cellLabel = 'Person';
101 |
102 | renderCell(item) {
103 | return (
104 |
;
116 | }
117 | }
118 | ```
119 |
120 | It's not really safe, because if `cellLabel` will change - Cell will not know about it and as all it's props are the same - it'll not re-render.
121 |
122 | Therefore you should use `use-method` mostly for events or something that dont impact rendering output.
123 |
124 | ## Licence
125 |
126 | MIT
127 |
--------------------------------------------------------------------------------
/demo/RendersCount.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | interface Props {
4 | label: string;
5 | }
6 |
7 | export function RenderCount({ label }: Props) {
8 | const countRef = React.useRef(0);
9 | countRef.current++;
10 |
11 | return (
12 |