{"id":18811085,"url":"https://github.com/ankit0183/react-spy-api","last_synced_at":"2026-01-24T11:04:34.408Z","repository":{"id":137279172,"uuid":"216329307","full_name":"ankit0183/React-Spy-API","owner":"ankit0183","description":"React Spy API For React Application ( Spy error, Intercept, BrodcastError etc...)A set of utilities for collecting UX-analytics of your React-application.!","archived":false,"fork":false,"pushed_at":"2024-08-27T23:54:40.000Z","size":45,"stargazers_count":2,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-07T23:28:50.823Z","etag":null,"topics":["ajax","analytics","hooks-api-react","json","spyware","vulnerability","vulnerability-scanners"],"latest_commit_sha":null,"homepage":"https://ankit0183.github.io/React-Spy-API/","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/ankit0183.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-20T08:18:41.000Z","updated_at":"2024-08-29T00:31:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"6a1cdd1b-0544-4f1b-8052-ec0748011245","html_url":"https://github.com/ankit0183/React-Spy-API","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankit0183%2FReact-Spy-API","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankit0183%2FReact-Spy-API/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankit0183%2FReact-Spy-API/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankit0183%2FReact-Spy-API/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ankit0183","download_url":"https://codeload.github.com/ankit0183/React-Spy-API/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231797661,"owners_count":18428061,"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":["ajax","analytics","hooks-api-react","json","spyware","vulnerability","vulnerability-scanners"],"created_at":"2024-11-07T23:24:18.322Z","updated_at":"2026-01-24T11:04:29.370Z","avatar_url":"https://github.com/ankit0183.png","language":"TypeScript","readme":"React Spy API\n---------\nReact Spy API For React Application ( Spy error, Intercept, BrodcastError etc...)\n\n```\nnpm i --save react-spy\n```\n\n### Features\n - Easy integration with any ui-library (ex: [Ant-Design](./examples/antd/))\n - Full control over the events\n\n---\n\n### API\n - [spy](#spy) — decorator of react-components\n   - [spy.send](#spy-send) — send stats from the component and not only\n   - [spy.error](#spy-error) — send an error from the component and not only\n - [addSpyObserver](#addSpyObserver) — add observer of events\n - [addSpyErrorObserver](#addSpyErrorObserver) — add observer of errors\n - [intercept](#intercept) — intercepting a chain of events\n - Components\n   - [Spy](#Spy)\n   - [SpyStep](#SpyStep)\n - Low Level\n   - [broadcast](#broadcast) — broadcast any chain of events\n   - [broadcastError](#broadcastError) — broadcast any error\n\n---\n\n### Usage\nFor example with Google Analytics\n\n```js\n// Btn.js\nimport {spy} from 'react-spy';\n\nconst Btn = ({name, value}) =\u003e (\u003cbutton name={name}\u003e{value}\u003c/button\u003e);\nexport default spy({\n\tid: ({name}) =\u003e name, // Computed `id` on based component properties\n\tlisten: ['click'],    // DOM-events list\n})(Btn);\n\n\n// LoginForm.js\nimport {spy} from 'react-spy';\n\nclass LoginForm extends React.Component {\n\t// ...\n\thandleSubmit(evt) {\n\t\tevt.preventDefault();\n\t\ttry {\n\t\t\tawait api.login(this.getFormData());\n\t\t\tspy.send(this, 'success');\n\t\t} catch (err) {\n\t\t\tspy.send(this, 'failed', err);\n\t\t}\n\t}\n\n\trender() {\n\t\treturn (\n\t\t\t\u003cform onSubmit={this.handleEvent}\u003e\n\t\t\t\t{/* ... */}\n\t\t\t\t\u003cBtn name=\"login\" value=\"Sign In\"/\u003e\n\t\t\t\t\u003cBtn name=\"forgot\" value=\"Forgot password\"/\u003e\n\t\t\t\u003c/form\u003e\n\t\t);\n\t}\n}\n\nexport default spy({\n\tid: \"login-form\",\n\thost: true,\n\tlisten: ['mount', 'unmount'],\n})(LoginForm);\n\n\n// boot.js\nimport {addSpyObserver, addSpyErrorObserver} from 'react-spy';\n\naddSpyObserver(chain =\u003e {\n\t// Send to GA\n\tga('send', {\n\t\thitType: 'event',\n\t\teventCategory: chain[0], // ex: \"login-form\"\n\t\teventAction: chain.slice(1).join('_'), // ex: \"forgot_click\"\n\t});\n});\n\n// Component Errors\naddSpyErrorObserver(({error}) =\u003e {\n\tga('send', 'exception', {\n\t\texDescription: error.message,\n\t\texFatal: false,\n\t});\n});\n\nReactDOM.render(\u003cApp/\u003e, document.body);\n```\n\n---\n\n\u003ca name=\"spy\"\u003e\u003c/a\u003e\n#### `spy\u003cProps\u003e(options)`\nDecorate the component to collect analytics\n\n - `options`\n   - **id**: `string | (props, context?) =\u003e string` — default @see `propName` description\n   - **propName**: `string` — prop-name for `id`, by default `spyId`\n   - **listen**: `string[]` — DOM-events to listen + `error`, `mount` and `unmount`\n   - **callbacks** — list of observed callbacks that are passed to it via `props`\n   - **propName**: `string` — name of the property responsible for the spy's `id`, by default` spyId`\n   - **host**: `boolean`\n\n```js\nimport {spy} from 'react-spy';\n\nexport default spy({\n\tid: ({name}) =\u003e name,\n\tlisten: ['click'],\n})(function Btn({value}) {\n\treturn \u003cbutton\u003e{value}\u003c/button\u003e;\n})\n\n// Somewhere in the code\n\u003cBtn\n\tname=\"login\"\n\tvalue=\"Sign in\"\n/\u003e\n// *click* -\u003e [\"login\", \"click\"]\n```\n\n---\n\n\u003ca name=\"spy-send\"\u003e\u003c/a\u003e\n#### `spy.send(cmp: React.Component, chain: string | string [], detail?: object): void`\nSend stats from the component and not only\n\n - **cmp**: `React.Component` — instance of `React.Component`\n - **chain**: `string | string[]` — name of metric\n - **detail**: `object`\n\n```js\nimport {spy} from 'react-spy';\n\nexport default spy({id: 'parent'})(class Box extends React.Component {\n\trender() {\n\t\treturn (\n\t\t\t\u003cbutton onClick={() =\u003e {spy.send(this, 'foo');}}\u003eFirst\u003c/button\u003e\n\t\t\t\u003cbutton onClick={() =\u003e {spy.send(this, ['bar', 'baz'], {val: 123});}}\u003eSecond\u003c/button\u003e\n\t\t);\n\t}\n});\n\n// Somewhere in a code\n//   click on \u003cFirst\u003e:\n//     - [\"parent\", \"foo\"] {}\n//   click on \u003cSecond\u003e:\n//     - [\"parent\", \"bar\", \"baz\"] {val: 123}\n//\n// Somewhere in an another place:\n//    spy.send(['global', 'label'], {time: 321}):\n//      - [\"global\", \"label\"] {time: 321}\n```\n\n---\n\n\u003ca name=\"spy-error\"\u003e\u003c/a\u003e\n#### `spy.error(cmp: React.Component, chain: string | string [], error: Error): void`\nsend an error from the component and not only\n\n - **cmp**: `React.Component` — instance of `React.Component`\n - **chain**: `string | string[]` — name of metric\n - **error**: `Error` — any an error\n\n---\n\n\u003ca name=\"addSpyObserver\"\u003e\u003c/a\u003e\n#### `addSpyObserver(fn: (chain: string[], detail: object) =\u003e void): UnsubsriberFn`\nAdd observer of events for sending to the accounting system of analytics\n\n```ts\nimport {addSpyObserver} from 'react-spy';\n\nconst unsubscribe = addSpyObserver(chain =\u003e {\n\t// Send to GA\n\tga('send', {\n\t\thitType: 'event',\n\t\teventCategory: chain[0], // ex: \"login-form\"\n\t\teventAction: chain.slice(1).join('_'), // ex: \"forgot_click\"\n\t});\n});\n\n// Somewhere (if you need to)\nunsubscribe();\n```\n\n---\n\n\u003ca name=\"addSpyErrorObserver\"\u003e\u003c/a\u003e\n#### `addSpyErrorObserver(fn: (detail: ErrorDetail) =\u003e void): UnsubsriberFn`\nAdd observer of component errors\n\n - `detail`\n   - **error**: `Error` — JavaScript error\n   - **info**: `object` — React error info\n   - **chain** `string[]` — spy `id` chain\n\n```ts\nimport {addSpyErrorObserver} from 'react-spy';\n\naddSpyErrorObserver(({error, chain}) =\u003e {\n\t// Send to GA\n\tga('send', 'exception', {\n\t\texDescription: error.message,\n\t\texFatal: false,\n\t});\n\n\t// For dev\n\tconsole.error('[react-spy]', chain.join(' -\u003e '));\n\tconsole.error(error);\n});\n```\n\n---\n\n\u003ca name=\"intercept\"\u003e\u003c/a\u003e\n#### `intercept(rules: InterceptRules)`\nIntercepting a chain of events\n\n```ts\nimport {intercept, UNCAUGHT} from 'react-spy';\n\nintercept({\n\t'login-form': {\n\t\t// Interception of all chains, ex:\n\t\t//  - [\"login-form\", \"forgot\", \"mount\"]\n\t\t//  - [\"login-form\", \"forgot\", \"click\"]\n\t\t//  - etc\n\t\t'forgot'(send, chain, detail) {\n\t\t\tsend(chain.concat('additional-id'));\n\t\t},\n\n\t\t// Processing of non-intercepted chains, ex:\n\t\t//  - [\"login-form\", \"login\", \"click\"]\n\t\t[UNCAUGHT](send, chain) {\n\t\t\tsend(chain.concat('UNCAUGHT'));\n\t\t\treturn false; // continue;\n\t\t}\n\t},\n});\n```\n\n---\n\n\u003ca name=\"Spy\"\u003e\u003c/a\u003e\n#### `\u003cSpy\u003e...\u003c/Spy\u003e`\n\n```jsx\nimport {Spy} from 'react-spy';\n\nconst SomeFragment = ({condition, onShowDetail}) =\u003e (\n\t\u003cdiv\u003e\n\t\t\u003cSpy id=\"top\"\u003e\n\t\t\t\u003cButton name=\"detail\" value=\"Show detail\" onClick={onShowDetail}/\u003e\n\t\t\u003c/Spy\u003e\n\n\t\t{condition \u0026\u0026\n\t\t\t\u003cSpy id=\"bottom\" listen={['mount', 'unmount'}\u003e\n\t\t\t\tDetail\n\t\t\t\u003c/Spy\u003e\n\t\t}\n\t\u003c/div\u003e\n);\n\n// 1. *click on button* -\u003e [\"top\", \"detail\", \"click\"]\n// 2. *mounting* -\u003e [\"bottom\", \"mount\"]\n```\n\n---\n\n\u003ca name=\"SpyStep\"\u003e\u003c/a\u003e\n#### `\u003cSpyStep name=\"...\"/\u003e`\nThe hidden spy element for steps monitoring\n\n - **name**: `string` — a step name\n - **enter**: `string | string[]` — the enter phase (optional)\n - **leave**: `string | string[]` — the leave phase (optional)\n\n---\n\n\u003ca name=\"broadcast\"\u003e\u003c/a\u003e\n#### `broadcast(chain: string[], detail?: object)`\n\n```ts\nimport {broadcast} from 'react-spy';\n\nbroadcast(['custom', 'event', 'chain'], {value: 'Wow'});\n// or just\n//   spy.send(['custom', 'event', 'chain'], {value: 'Wow'})\n```\n\n---\n\n\n\u003ca name=\"broadcastError\"\u003e\u003c/a\u003e\n#### `broadcastError(detail: ErrorDetail)`\n\n - `detail`\n   - **error**: `Error` — JavaScript error\n   - **chain** `string[]` — spy `id` chain\n   - **info**: `object` — React error info (optional)\n\n\n```ts\nimport {broadcastError} from 'react-spy';\n\nbroadcastError({\n\tchain: ['login', 'submit', 'failed'],\n\terror: new Error('Internal Error'),\n});\n// or just\n//   spy.error('localStorage', new Error('Read'));\n//   spy.error(thisReactCmp, 'localStorage', new Error('save'));\n```\n\n---\n\n\n### Development\n\n - `npm i`\n - `npm test`, [code coverage](./coverage/lcov-report/index.html)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fankit0183%2Freact-spy-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fankit0183%2Freact-spy-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fankit0183%2Freact-spy-api/lists"}