├── .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 | [![Build Status](https://travis-ci.org/ilyalesik/react-use-trigger.svg?branch=master)](https://travis-ci.org/ilyalesik/react-use-trigger) 4 | [![npm version](https://img.shields.io/npm/v/react-use-trigger.svg)](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 | Sponsored by Lessmess 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 | --------------------------------------------------------------------------------