{"id":18206717,"url":"https://github.com/realamirhe/react-aptor","last_synced_at":"2025-04-02T15:30:41.224Z","repository":{"id":40506857,"uuid":"359209727","full_name":"realamirhe/react-aptor","owner":"realamirhe","description":"Make Forward compatible React packages🔮","archived":false,"fork":false,"pushed_at":"2023-07-19T02:11:48.000Z","size":1105,"stargazers_count":31,"open_issues_count":4,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-17T04:36:14.377Z","etag":null,"topics":["api","package","react","react-aptor"],"latest_commit_sha":null,"homepage":"https://ahimico.github.io/docs/react-aptor/tutorials-overview","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/realamirhe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2021-04-18T17:26:17.000Z","updated_at":"2024-12-18T10:21:29.000Z","dependencies_parsed_at":"2024-06-18T18:36:26.215Z","dependency_job_id":"5c343399-c9b0-45e9-8fc9-121429996348","html_url":"https://github.com/realamirhe/react-aptor","commit_stats":{"total_commits":126,"total_committers":3,"mean_commits":42.0,"dds":0.05555555555555558,"last_synced_commit":"b5c4dcb74ed210860e1f3e9081de92fb77eb8cc1"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/realamirhe%2Freact-aptor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/realamirhe%2Freact-aptor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/realamirhe%2Freact-aptor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/realamirhe%2Freact-aptor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/realamirhe","download_url":"https://codeload.github.com/realamirhe/react-aptor/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246841514,"owners_count":20842606,"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":["api","package","react","react-aptor"],"created_at":"2024-11-03T12:06:05.472Z","updated_at":"2025-04-02T15:30:40.560Z","avatar_url":"https://github.com/realamirhe.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eReact Aptor\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://github.com/realamirhe/react-aptor/raw/master/doc/assets/logo.svg\" alt=\"react aptor logo\" width=\"450\"\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003e\nMinimal API Connector for react\n  \u003cbr\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Tree%20Shakeable-d4e157\" alt=\"tree-shakeable\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Zero%20Dependencies-ffbfbf\" alt=\"zero dependencies\" /\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Side%20Effect%20Free-ffeb3b\" alt=\"side-effect free\" /\u003e\n\u003c/p\u003e\t\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://npmjs.org/package/react-aptor\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dt/react-aptor.svg\" alt=\"downloads\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/result?p=react-aptor\"\u003e\n    \u003cimg src=\"https://img.shields.io/bundlephobia/minzip/react-aptor.svg\" alt=\"bundle size\" /\u003e\n  \u003c/a\u003e\n \u003c!-- \u003ca href=\"https://lgtm.com/projects/g/realamirhe/react-aptor/context:javascript\"\u003e\n    \u003cimg src=\"https://img.shields.io/lgtm/grade/javascript/g/realamirhe/react-aptor.svg?logo=lgtm\u0026logoWidth=18\" alt=\"Language grade: JavaScript\" /\u003e\n  \u003c/a\u003e --\u003e\n  \u003ca href=\"https://paka.dev/npm/react-aptor@latest/api\"\u003e\n    \u003cimg src=\"https://paka.dev/badges/v0/cute.svg\" alt=\"Docs\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nEnglish | \u003ca href=\"https://github.com/realamirhe/react-aptor/blob/master/doc/localization/fa.md\"\u003ePersian\u003c/a\u003e | (\u003ca href=\"https://github.com/realamirhe/react-aptor/issues/new\"\u003eadd your language\u003c/a\u003e)\n\nDon’t waste your time by finding react version of your favorite javascript package, keep control of your `API` now.\n\n**Documentation**: You can find the react-aptor documentation [on the website](https://ahimico.github.io/docs/react-aptor/tutorials-overview).\n\n## Why\n\nMost packages are developed separately in JavaScript for increasing generality being library/framework agnostic.\n\nConnecting vanilla third parties to react is not a routine task especially for those that need to change the DOM.\nOn the other hand, these packages might be developed by different teams, hence development progress can be one step behind the original or even be terminated at any time. Also, wrong abstraction or bad design patterns may interrupt the progress of these `react-xyz` packages.\n\nIf you are still not convinced you can read this [article](https://dev.to/amirhe/aptor-is-all-you-need-3din)\n\n**Other Concerns**:\n\n- Finding DOM nodes by ReactDOM.findDOMNode\n- Extensively usage of memorization to improve performance or prevent extra re-renders\n- Another duplication layer for all API definitions in react that increase the project size\n- Rely on a global scope (e.g. window) for internal settings (making it impossible to have more than one instance)\n- backward compatible updates of the base package need another update for the react-xyz package\n\n## react-aptor\n\nWe strive to solve all mentioned problems at once and for all.\n\n## Features\n\n\u003cdetails\u003e\n\t\u003csummary\u003eSmall\u003c/summary\u003e\n    Zero-dependency with less than 1 kilobyte in size (327 B 😱) \u003ca href=\"https://bundlephobia.com/result?p=react-aptor\"\u003ereact-aptor\u003c/a\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n    \u003csummary\u003eManageable\u003c/summary\u003e\nYour used/defined APIs are entirely under your control. Make it possible to define a slice of APIs that you are surely going to use.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n    \u003csummary\u003eReact-ish\u003c/summary\u003e\n    Developed with lots of care, try to be zero-anti-pattern in react.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n    \u003csummary\u003eSimple\u003c/summary\u003e\n    It's as simple as that. 💛\n\u003c/details\u003e\n\n\u003cdetails\u003e\n    \u003csummary\u003eTypescript\u003c/summary\u003e\n    Feel free to contribute or suggest any changes in \u003ca href=\"https://github.com/amirHossein-Ebrahimi/react-aptor/issues\"\u003eissues page\u003c/a\u003e\n\u003c/details\u003e\n\n## How to use\n\nConnect your react app to any third party in three-step\n\n1. Define the instantiate function\n2. Define the get API function\n3. Connect API to react by `useAptor`\n\n---\n\n1. Define the **instantiate** function.\n\n```js\nimport Something from 'some-third-party';\n\nexport default function instantiate(node, params) {\n  return new Something(node, params);\n}\n```\n\nThis function will return an instance of the third-party package. You have access to node (DOM-node\\*) and params.\n\n\u003e The `node` is passed by react-aptor as a reference to DOM that is occasionally used as a wrapper for embedding UI.\n\u003e The DOM-node\\* will become more clear in the third step.\n\u003e\n\u003e The `params` are optional parameters that are passed by react-aptor and defined by you. see the third step.\n\u003e The params will be passed by you and will be more clear in the third step.\n\u003e\n\u003e name this file **construct.js** as convention ✨.\n\n2. Define the **get API** function.\n\n```js\nexport default function getAPI(instance, params) {\n  // return corresponding API Object\n  return () =\u003e ({\n    api_key: () =\u003e {\n      /* api definition using instance and params */\n      console.log(instance);\n    },\n  });\n}\n```\n\nThe react-aptor will pass the latest instance of your third party which has been defined in the first step by the **instantiate** function along with **params** to **getAPI** function.\n\n\u003e The `instance` is returned instance of your third party.\n\u003e Technically it is exactly going to be **instantiate(node, params)**\n\u003e\n\u003e The `params` are optional parameters that are passed by react-aptor and defined by you. see the third step.\n\u003e The params will be passed by you and will be more clear in the third step.\n\u003e\n\u003e name this file **api.js** as convention ✨.\n\n3. **Connect API to react** by `useAptor`\n\n```jsx\nimport useAptor from 'react-aptor';\nimport getAPI from './api';\nimport instantiate from './construct';\n\nconst Connector = (props, ref) =\u003e {\n  const aptorRef = useAptor(ref, {\n    getAPI,\n    instantiate,\n    /* params: anything */\n  });\n\n  return \u003cdiv ref={aptorRef} /\u003e;\n};\n\nexport default React.forwardRef(Connector);\n```\n\n\u003e name this file **connector.jsx** as convention ✨\n\u003e If you are using react 17 or newer version, you can also name it **connector.js**\n\n**useAptor in one look**\n\n```jsx\nconst aptorRef = useAptor(ref, configuration, deps);\n```\n\n**ref**\nFor the connection phase, you need to define a **forwardRef** component. The `useAptor` hook needs forwarded-ref as the first argument, this is necessary to bind all your defined API to this ref.\n\n**configuration**\nAs the configuration argument you need to pass defined **instantiate** (defined in the first step ☝️), **getAPI** (defined in the second step ☝️) and your custom params argument. The useAptor hook will return you a ref (`aptorRef`) which you can bind to your DOM node.\n\n\u003e The `params` doesn't have any limitation, it can be any arbitrary type e.g. undefined, number, string, or an object containing all of them. The params will be then passed to your instantiate and getAPI function, as you saw in the first and second steps.\n\u003e Params is the best place to connect props to your low-level API it means ”No Need” for extra function generation 🥳\n\n**deps**\nIs the same as Dependencies array default value is `[]` but you can override it as the third and last argument of useAptor. It may be needed in a situation in which you want to force re-instantiate by some prop change. It will use shallow comparison (as react do) for dependency array and will call your `instantiate` \u0026 `getAPI` in a row.\n\n### API usage\n\n```jsx\nconst Main = () =\u003e {\n  const ref = createRef();\n\n  const apiKeyHandler = () =\u003e {\n    if (ref.current) {\n      ref.current.api_key();\n    }\n  };\n\n  return (\n    \u003cdiv\u003e\n      \u003cConnector ref={ref} /\u003e\n      \u003cButton onClick={apiKeyHandler}\u003eapi call\u003c/Button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\nPass **createRef** to the Connector component (made in the third step), and then you can access all of the APIs inside **ref.current**\n\n### 💡 Using of optional chaining\n\n\u003e function call can be much more readable with [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) \u0026 related [babel plugin](https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining)\n\n```jsx\nconst apiKeyHandler = () =\u003e ref.current?.api_key();\n```\n\n### 💡 Better naming\n\n\u003e In case you need `ref.current` more than one time, it is a good idea to rename it in the first place\n\n```jsx\nconst apiKeyHandler = () =\u003e {\n  const { current: api } = ref; // store ref.current in `api`\n  if (api) {\n    api.api_key();\n  }\n};\n```\n\n### 💡 Can I remove if check-in handlers\n\nCause the default value for ref can be undefined (in **createRef**) and null (in **useRef**) Typescript will complain about possibility for not-existence of apis. [see more](https://fettblog.eu/typescript-react/hooks/#useref).\nIn a normal world react will bind your API to the given ref after the Connector mount\n\nIf you're using ref in useEffect or somewhere which is guaranteed to have the ref bounded to values, you can return a proxy object in your `getAPI` function to bind all api functions simultaneously.\n\n```js\nexport default function getAPI(thirdParty, params) {\n  if (!thirdParty)\n    return () =\u003e\n      new Proxy(\n        {},\n        {\n          get: (_, prop) =\u003e {\n            // Possible to mock differently for different props\n            return noop;\n          },\n        }\n      );\n\n  return () =\u003e ({\n    api_key() {\n      // third-party is defined here for sure :)\n      console.log(thirdParty);\n    },\n  });\n}\n```\n\n### 💡 Micro API instructions\n\n\u003e You can access all of your APIs via `this` keyword\n\n```js\nexport default function getAPI(sound, params) {\n  return () =\u003e ({\n    _state() {\n      return sound.getState();\n    },\n\n    play() {\n      if (this._state() === 'LOADED') sound.play();\n    },\n  });\n}\n```\n\n\u003e It's better to start the name of these internal functions with `_`\n\n### 💡 The `this` problem in API object\n\nIn a case, you see this keyword usage in third-party API\nyou must specify `this` something other than returned API object.\nThe following examples are for howler integration using react-aptor:\n\n```js\n{\n  // ❌ It doesn't work\n  state: howler.state,\n\n  // 🆗 this is Okay\n  state: howler.state.bind(howler),\n  // 🆗 this is also Okay\n  state: () =\u003e howler.state(),\n  // 🆗 this is also Okay, too\n  state() {\n    return howler.state();\n  }\n}\n```\n\n### 💡 How to get API-Object type\n\nYou can use something like the follwing:\n\n```ts\nexport type APITypes = ReturnType\u003cReturnType\u003ctypeof getAPI\u003e\u003e;\n```\n\n### 💡 How to make a custom react integration package using react-aptor\n\n1. Name your package **raptor-something** :)\n2. Use minimum possible configuration for your API\n3. Interact to props change in your component using `useEffect` and proper `deps` array\n4. Bind another ref to your Connector using the fork-ref idea\n\nFor another possible usage see our production ready examples:\n\n- **[plyr-react](https://github.com/chintan9/plyr-react)**\n- **[raptor-howler-example](https://github.com/amirHossein-Ebrahimi/raptor-howler/tree/master/example)**\n\n## core\n\n### **ref** _`required`_\n\nThe react **useRef** or **createRef** ref instance which has been passed throw **react.forwardRef** method.\nyour API will be stored in this ref.\n\n### **configuration** _`required`_\n\n- ### **instantiate** _`required`_\n\n  \u003e function(node, params): Instance\n\n  A function that receives probable bounded-node and params. It then returns an instance of your third party.\n\n- ### **destroy**\n\n  \u003e function(previousInstance, params): void\n\n  A function that receives previously created instances and params. It is useful when you want to perform the cleanup before new instance creation. e.g. **remove event listeners**, **free up allocated memories**, **destroy internally** \u0026 etc\n\n- ### **getAPI** _`required`_\n\n  \u003e function(Instance, params): ApiObject\n\n  A function that receives instances of your third-party and params. It then returns a key-value pair object for API handlers.\n\n- ### **params** `any`\n\n  Params can have any arbitrary type and can be used with props or pre-defined options.\n\n### **deps** `[]`\n\nReact dependencies array for re-instantiating your third-party packages. It will call `instantiate` with the latest node, params whenever shallow comparison for with the previous deps array finds inequality.\n\n---\n\n## **Donation**\n\n💻 Developer/Maintainer (**TRC20 - usdt**):\n`TTa9zzsdRMV8BntNUZ4BYvrzBzUTfFKbL8`\n\n🎨 Designer (**BTC**):\n`bc1q9fahyct3lrdz47pjf4kfxvsyum2dm74v2hv9xl`\n\n## Color Palettes\n\n![#c7589e](https://i.stack.imgur.com/YuJd9.png '#c7589e')\n![#ed5b6e](https://i.stack.imgur.com/7ssUA.png '#ed5b6e')\n![#f9b919](https://i.stack.imgur.com/31VUW.png '#f9b919')\n![#26abe2](https://i.stack.imgur.com/y3o2u.png '#26abe2')\n![#129f4d](https://i.stack.imgur.com/mWeRk.png '#129f4d')\n\n## Samples\n\n**NOTE**: Add your favorite package to the following list by creating a [new issue](https://github.com/realamirhe/react-aptor/issues).\n\n\u003ca href=\"https://codesandbox.io/s/react-aptor--fabric-hp50c\" title=\"codesandbox | fabric.js\"\u003e\n  \u003cimg src=\"https://i.stack.imgur.com/GLnt7.png\" width=\"150\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://codesandbox.io/s/react-aptor--quill-iqwcd\" title=\"codesandbox | quill.js\"\u003e\n  \u003cimg src=\"https://i.stack.imgur.com/wVBVM.png\" width=\"150\"\u003e\n\u003c/a\u003e\n\u0026nbsp;\u0026nbsp;\n\u003ca href=\"https://github.com/realamirhe/raptor-howler\" title=\"github | react-howler\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/realamirhe/raptor-howler/master/doc/assets/logo.svg\" width=\"150\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://stackblitz.com/edit/react-aptor-rivejs\" title=\"stackblitz | rive.js\"\u003e\n  \u003cimg src=\"https://i.stack.imgur.com/SYdgf.png\" width=\"150\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://codesandbox.io/s/react-aptor--howler-4o8t4\" title=\"codesandbox | howler.js\"\u003e\n  \u003cimg src=\"https://i.stack.imgur.com/bmvAc.png\" width=\"150\" height=\"150\"\u003e\n\u003c/a\u003e\n\n\u003cp align=\"center\"\u003e\n   \u003ca href=\"./doc/samples.md\"\u003e ⭐ SEE ALL SAMPLES 🌟\u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frealamirhe%2Freact-aptor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frealamirhe%2Freact-aptor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frealamirhe%2Freact-aptor/lists"}