├── .babelrc
├── .eslintignore
├── .flowconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── index.d.ts
├── index.js
├── index.js.flow
├── jest
└── setupTests.js
├── package-lock.json
├── package.json
├── tests
├── useTrigger.test.js
└── useTriggerEffect.test.js
├── tsconfig.json
├── types
├── TriggerWrapper.d.ts
└── TriggerWrapper.js
├── useTrigger.d.ts
├── useTrigger.js
├── useTrigger.js.flow
├── useTriggerEffect.d.ts
├── useTriggerEffect.js
└── useTriggerEffect.js.flow
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-flow"],
3 | "plugins": [
4 | ["@babel/plugin-transform-runtime",
5 | {
6 | "regenerator": true
7 | }
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .cache/
2 | dist/
3 | node_modules/
4 | flow-typed/
5 | tests/
6 | types/
7 |
--------------------------------------------------------------------------------
/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 |
3 | [include]
4 |
5 | [libs]
6 | ./flow-typed
7 |
8 | [lints]
9 |
10 | [options]
11 | module.file_ext=.js
12 | module.file_ext=.jsx
13 |
14 | [strict]
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | npm-debug.log
3 |
4 | .cache/
5 | dist/
6 | node_modules/
7 | .idea/
8 | .vscode/
9 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *.log
2 | npm-debug.log*
3 |
4 | # Coverage directory used by tools like istanbul
5 | coverage
6 | .nyc_output
7 |
8 | # Dependency directories
9 | node_modules
10 |
11 | # npm package lock
12 | package-lock.json
13 | yarn.lock
14 |
15 | # project files
16 | src
17 | tests
18 | jest
19 | .travis.yml
20 | .babelrc
21 | .gitignore
22 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '8'
4 | branches:
5 | only:
6 | - master
7 | cache:
8 | directories:
9 | - node_modules
10 | before_install:
11 | - npm update
12 | install:
13 | - npm install
14 | script:
15 | - npm run flow:check
16 | - npm test
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Ilya Lesik
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 | # react-use-trigger
2 |
3 | [](https://travis-ci.org/ilyalesik/react-use-trigger)
4 | [](https://www.npmjs.com/package/react-use-trigger)
5 |
6 | React Hook for trigger effect from any place of code. It is an implementation a Pub-Sub strategy on React Hooks.
7 |
8 | * **Small** 305 bytes (minified and gzipped).
9 | * Both **Flow** and **TS** types included.
10 |
11 | ```javascript
12 | import createTrigger from "react-use-trigger";
13 | import useTriggerEffect from "react-use-trigger/useTriggerEffect";
14 |
15 | const fooTrigger = createTrigger();
16 |
17 | export const Subscriber = () => {
18 | useTriggerEffect(() => {
19 | // make some effect
20 | }, fooTrigger);
21 |
22 | return
;
23 | }
24 |
25 | export const Sender = () => {
26 | return
29 | }
30 | ```
31 |
32 | Also, `useTrigger` may be used for combine with other inputs:
33 | ```javascript
34 | export const Subscriber = (props) => {
35 | const fooTriggerValue = useTrigger(fooTrigger);
36 | const [someState, setSomeState] = useState();
37 |
38 | useEffect(() => {
39 | // make some effect
40 | }, [fooTriggerValue, props.someProp, someState]);
41 |
42 | return ;
43 | }
44 | ```
45 |
46 |
47 |
49 |
50 |
51 | ## Installation
52 |
53 | Install it with yarn:
54 |
55 | ```
56 | yarn add react-use-trigger
57 | ```
58 |
59 | Or with npm:
60 |
61 | ```
62 | npm i react-use-trigger --save
63 | ```
64 |
65 | ## API
66 |
67 | #### `createTrigger(): TriggerWrapper;`
68 | Create a trigger.
69 | `TriggerWrapper` is function, that update value of trigger.
70 |
71 |
72 | #### `useTrigger(triggerWrapper: TriggerWrapper): string;`
73 |
74 | Returns current value of trigger. A string, generated by [nanoid](https://github.com/ai/nanoid).
75 |
76 | Can be used for combine trigger with other inputs:
77 | ```javascript
78 | import useTrigger from "react-use-trigger/useTrigger";
79 |
80 | export const Subscriber = (props) => {
81 | const fooTriggerValue = useTrigger(fooTrigger);
82 | const [someState, setSomeState] = useState();
83 |
84 | useEffect(() => {
85 | // make some effect
86 | }, [fooTriggerValue, props.someProp, someState]);
87 |
88 | return ;
89 | }
90 | ```
91 |
92 | #### `useTriggerEffect(create: () => MaybeCleanUpFn, triggerWrapper: TriggerWrapper): void;`
93 |
94 | Call effect (from `create`) for every change of trigger.
95 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import { TriggerWrapper} from "./types/TriggerWrapper"
2 | declare function createTrigger(): TriggerWrapper;
3 |
4 | export = createTrigger;
5 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var nanoid = require('nanoid')
2 |
3 | function createTrigger () {
4 | var trigger = function () {
5 | trigger.id = nanoid()
6 | trigger.subscribers.forEach(function (subscriber) {
7 | subscriber()
8 | })
9 | }
10 |
11 | trigger.id = nanoid()
12 | trigger.subscribers = []
13 |
14 | trigger.subscribe = function (f) {
15 | trigger.subscribers.push(f)
16 | }
17 |
18 | trigger.unsubscribe = function (f) {
19 | trigger.subscribers.indexOf(f) >= 0 &&
20 | trigger.subscribers.splice(trigger.subscribers.indexOf(f), 1)
21 | }
22 |
23 | return trigger
24 | }
25 |
26 | module.exports = createTrigger
27 |
--------------------------------------------------------------------------------
/index.js.flow:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { TriggerWrapper } from "./types/TriggerWrapper";
3 | declare export default function createTrigger(): TriggerWrapper;
4 |
--------------------------------------------------------------------------------
/jest/setupTests.js:
--------------------------------------------------------------------------------
1 | require('jest-dom/extend-expect')
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-use-trigger",
3 | "version": "0.3.4",
4 | "description": "React effect trigger hook",
5 | "scripts": {
6 | "flow": "flow",
7 | "flow:check": "flow check",
8 | "test": "jest --silent",
9 | "typescript": "tsc -p tsconfig.json --noEmit",
10 | "check-size": "size-limit",
11 | "lint": "eslint '**/*.{js,jsx}' --quiet",
12 | "prepublish": "npm run flow:check && npm run test && npm run lint && npm run check-size && npm run typescript"
13 | },
14 | "keywords": [
15 | "react",
16 | "hook"
17 | ],
18 | "eslintConfig": {
19 | "extends": "@logux/eslint-config/browser",
20 | "rules": {
21 | "node/no-unpublished-require": "off",
22 | "es5/no-es6-static-methods": "off",
23 | "node/no-missing-require": "off",
24 | "func-style": "off"
25 | }
26 | },
27 | "jest": {
28 | "setupTestFrameworkScriptFile": "./jest/setupTests.js",
29 | "moduleNameMapper": {
30 | "\\.(svg|png)$": "/__mocks__/fileMock.js"
31 | }
32 | },
33 | "files": [
34 | "types",
35 | "index.js",
36 | "index.js.flow",
37 | "index.d.ts",
38 | "useTrigger.js",
39 | "useTrigger.js.flow",
40 | "useTrigger.d.ts",
41 | "useTriggerEffect.js",
42 | "useTriggerEffect.js.flow",
43 | "useTriggerEffect.d.ts"
44 | ],
45 | "pre-commit": [
46 | "lint",
47 | "flow:check",
48 | "test",
49 | "check-size"
50 | ],
51 | "repository": {
52 | "type": "git",
53 | "url": "git+ssh://git@github.com/ilyalesik/react-use-trigger.git"
54 | },
55 | "size-limit": [
56 | {
57 | "path": "index.js",
58 | "limit": "236 B"
59 | },
60 | {
61 | "path": [
62 | "index.js",
63 | "useTrigger.js"
64 | ],
65 | "limit": "305 B",
66 | "ignore": [
67 | "react"
68 | ]
69 | },
70 | {
71 | "path": [
72 | "index.js",
73 | "useTrigger.js",
74 | "useTriggerEffect.js"
75 | ],
76 | "limit": "330 B",
77 | "ignore": [
78 | "react"
79 | ]
80 | }
81 | ],
82 | "author": "Ilya Lesik ",
83 | "license": "MIT",
84 | "bugs": {
85 | "url": "https://github.com/ilyalesik/react-use-trigger/issues"
86 | },
87 | "homepage": "https://github.com/ilyalesik/react-use-trigger#readme",
88 | "dependencies": {
89 | "nanoid": "^2.0.1"
90 | },
91 | "peerDependencies": {
92 | "react": ">=16.8.0"
93 | },
94 | "devDependencies": {
95 | "@babel/cli": "^7.2.3",
96 | "@babel/core": "^7.4.0",
97 | "@babel/plugin-transform-runtime": "^7.4.0",
98 | "@babel/preset-env": "^7.4.2",
99 | "@babel/preset-flow": "^7.0.0",
100 | "@babel/preset-react": "^7.0.0",
101 | "@logux/eslint-config": "^28.2.0",
102 | "eslint": "^5.16.0",
103 | "eslint-config-standard": "^12.0.0",
104 | "eslint-plugin-es5": "^1.3.1",
105 | "eslint-plugin-import": "^2.17.2",
106 | "eslint-plugin-import-helpers": "^0.1.4",
107 | "eslint-plugin-jest": "^22.5.1",
108 | "eslint-plugin-node": "^8.0.1",
109 | "eslint-plugin-prefer-let": "^1.0.1",
110 | "eslint-plugin-promise": "^4.1.1",
111 | "eslint-plugin-security": "^1.4.0",
112 | "eslint-plugin-standard": "^4.0.0",
113 | "flow-bin": "^0.95.1",
114 | "jest": "^24.5.0",
115 | "jest-dom": "^3.1.3",
116 | "pre-commit": "^1.2.2",
117 | "react": "^16.8.6",
118 | "react-dom": "^16.8.6",
119 | "react-testing-library": "^6.0.3",
120 | "size-limit": "^1.3.1",
121 | "typescript": "^3.4.5"
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/tests/useTrigger.test.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { render, wait } from "react-testing-library";
3 | import createTrigger from "../index";
4 | import useTrigger from "../useTrigger";
5 |
6 | describe("useTrigger", () => {
7 | it("call trigger", async () => {
8 | const trigger = createTrigger();
9 | const effect = jest.fn();
10 |
11 | const Component = () => {
12 | const triggerValue = useTrigger(trigger);
13 |
14 | useEffect(() => {
15 | effect();
16 | }, [triggerValue]);
17 |
18 | return ;
19 | };
20 |
21 | const { container, rerender } = render();
22 |
23 | await wait(() => {
24 | trigger();
25 | });
26 |
27 | await wait(() => {
28 | rerender();
29 |
30 | expect(effect.mock.calls.length).toEqual(2);
31 | });
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/tests/useTriggerEffect.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render, wait } from "react-testing-library";
3 | import createTrigger from "../index";
4 | import useTriggerEffect from "../useTriggerEffect";
5 |
6 | describe("useTriggerEffect", () => {
7 | it("call trigger", async () => {
8 | const trigger = createTrigger();
9 | const effect = jest.fn();
10 |
11 | const Component = () => {
12 | useTriggerEffect(() => {
13 | effect();
14 | }, trigger);
15 |
16 | return ;
17 | };
18 |
19 | const { container, rerender } = render();
20 |
21 | await wait(() => {
22 | trigger();
23 | });
24 |
25 | await wait(() => {
26 | rerender();
27 |
28 | expect(effect.mock.calls.length).toEqual(2);
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "jsx": "react",
5 | "lib": [
6 | "dom",
7 | "es2018"
8 | ],
9 | "module": "esnext",
10 | "moduleResolution": "node",
11 | "noImplicitAny": false,
12 | "noImplicitReturns": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "strict": true,
16 | "strictFunctionTypes": false,
17 | "stripInternal": true
18 | },
19 | "include": [
20 | "**/*.d.ts"
21 | ],
22 | "exclude": [
23 | "node_modules"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/types/TriggerWrapper.d.ts:
--------------------------------------------------------------------------------
1 |
2 | export type TriggerWrapper = {
3 | id: string;
4 | subscribe: ((f: (() => unknown)) => void);
5 | unsubscribe: ((f: (() => unknown)) => void);
6 | (): void
7 | }
8 |
--------------------------------------------------------------------------------
/types/TriggerWrapper.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | export type TriggerWrapper = {
3 | id: string,
4 | subscribe: (f: () => mixed) => void,
5 | unsubscribe: (f: () => mixed) => void,
6 | (): void,
7 | };
--------------------------------------------------------------------------------
/useTrigger.d.ts:
--------------------------------------------------------------------------------
1 | import { TriggerWrapper} from "./types/TriggerWrapper"
2 | declare function useTrigger(trigger: TriggerWrapper): string;
3 |
4 | export = useTrigger;
5 |
--------------------------------------------------------------------------------
/useTrigger.js:
--------------------------------------------------------------------------------
1 | var React = require('react')
2 |
3 | function useTrigger (trigger) {
4 | var state = React.useState(trigger.id)
5 |
6 | var update = function () { return state[1](trigger.id) }
7 |
8 | React.useEffect(function () {
9 | trigger.subscribe(update)
10 | return function () { return trigger.unsubscribe(update) }
11 | }, [])
12 |
13 | return state[0]
14 | }
15 |
16 | module.exports = useTrigger
17 |
--------------------------------------------------------------------------------
/useTrigger.js.flow:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { TriggerWrapper } from "./types/TriggerWrapper";
3 | declare export default function useTrigger(trigger: TriggerWrapper): string;
4 |
--------------------------------------------------------------------------------
/useTriggerEffect.d.ts:
--------------------------------------------------------------------------------
1 | import { TriggerWrapper} from "./types/TriggerWrapper"
2 | type MaybeCleanUpFn = (void | (() => void))
3 | declare function useTriggerEffect(create: (() => MaybeCleanUpFn), trigger: TriggerWrapper): void;
4 |
5 | export = useTriggerEffect;
6 |
--------------------------------------------------------------------------------
/useTriggerEffect.js:
--------------------------------------------------------------------------------
1 | var React = require('react')
2 |
3 | var useTrigger = require('./useTrigger')
4 |
5 | function useTriggerEffect (create, trigger) {
6 | var triggerValue = useTrigger(trigger)
7 |
8 | React.useEffect(create, [triggerValue])
9 | }
10 |
11 | module.exports = useTriggerEffect
12 |
--------------------------------------------------------------------------------
/useTriggerEffect.js.flow:
--------------------------------------------------------------------------------
1 | // @flow
2 | import type { TriggerWrapper } from "./types/TriggerWrapper";
3 | type MaybeCleanUpFn = void | (() => void);
4 | declare export default function useTriggerEffect(create: () => MaybeCleanUpFn, trigger: TriggerWrapper): void;
5 |
--------------------------------------------------------------------------------