Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-aptor

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-aptor - npm Package Compare versions

Comparing version 1.1.2 to 1.2.0

11

esm/useAptor.d.ts

@@ -1,13 +0,12 @@

import { ForwardedRef, RefObject } from 'react';
import { RefObject, Ref } from 'react';
declare type Nullable<T> = T | null;
export declare type SingleAPI = (...args: any[]) => any;
export declare type APIObject = {
[apiName: string]: SingleAPI;
};
export declare type APIObject = Record<string, any>;
export declare type APIGenerator = () => APIObject;
export declare type GetAPI<T> = (instance: Nullable<T>, prams?: any) => APIGenerator;
export declare type Instantiate<T> = (node: Nullable<HTMLElement>, params?: any) => Nullable<T>;
export declare type Destroy<T> = (instance: Nullable<T>, params?: any) => void;
export interface AptorConfiguration<T> {
getAPI: GetAPI<T>;
instantiate: Instantiate<T>;
destroy: Destroy<T>;
params?: any;

@@ -22,3 +21,3 @@ }

*/
export default function useAptor<T>(ref: ForwardedRef<APIObject>, configuration: AptorConfiguration<T>, deps?: never[]): RefObject<HTMLElement>;
export default function useAptor<T>(ref: Ref<APIObject>, configuration: AptorConfiguration<T>, deps?: any[]): RefObject<HTMLElement>;
export {};

@@ -1,2 +0,2 @@

import { useEffect, useImperativeHandle, useState, useRef, useMemo, } from 'react';
import { useEffect, useImperativeHandle, useState, useRef, useMemo } from 'react';
/**

@@ -13,6 +13,11 @@ * react aptor(api-connector) a hook which connect api to react itself

var domRef = useRef(null);
var instantiate = configuration.instantiate, getAPI = configuration.getAPI, params = configuration.params;
var instantiate = configuration.instantiate, destroy = configuration.destroy, getAPI = configuration.getAPI, params = configuration.params;
useEffect(function () {
setInstance(instantiate(domRef.current, params));
var instanceReference = instantiate(domRef.current, params);
setInstance(instanceReference);
// eslint-disable-next-line react-hooks/exhaustive-deps
return function () {
if (destroy)
destroy(instanceReference, params);
};
}, deps);

@@ -19,0 +24,0 @@ // eslint-disable-next-line react-hooks/exhaustive-deps

@@ -1,13 +0,12 @@

import { ForwardedRef, RefObject } from 'react';
import { RefObject, Ref } from 'react';
declare type Nullable<T> = T | null;
export declare type SingleAPI = (...args: any[]) => any;
export declare type APIObject = {
[apiName: string]: SingleAPI;
};
export declare type APIObject = Record<string, any>;
export declare type APIGenerator = () => APIObject;
export declare type GetAPI<T> = (instance: Nullable<T>, prams?: any) => APIGenerator;
export declare type Instantiate<T> = (node: Nullable<HTMLElement>, params?: any) => Nullable<T>;
export declare type Destroy<T> = (instance: Nullable<T>, params?: any) => void;
export interface AptorConfiguration<T> {
getAPI: GetAPI<T>;
instantiate: Instantiate<T>;
destroy: Destroy<T>;
params?: any;

@@ -22,3 +21,3 @@ }

*/
export default function useAptor<T>(ref: ForwardedRef<APIObject>, configuration: AptorConfiguration<T>, deps?: never[]): RefObject<HTMLElement>;
export default function useAptor<T>(ref: Ref<APIObject>, configuration: AptorConfiguration<T>, deps?: any[]): RefObject<HTMLElement>;
export {};

@@ -15,6 +15,11 @@ "use strict";

var domRef = react_1.useRef(null);
var instantiate = configuration.instantiate, getAPI = configuration.getAPI, params = configuration.params;
var instantiate = configuration.instantiate, destroy = configuration.destroy, getAPI = configuration.getAPI, params = configuration.params;
react_1.useEffect(function () {
setInstance(instantiate(domRef.current, params));
var instanceReference = instantiate(domRef.current, params);
setInstance(instanceReference);
// eslint-disable-next-line react-hooks/exhaustive-deps
return function () {
if (destroy)
destroy(instanceReference, params);
};
}, deps);

@@ -21,0 +26,0 @@ // eslint-disable-next-line react-hooks/exhaustive-deps

{
"name": "react-aptor",
"version": "1.1.2",
"version": "1.2.0",
"private": false,

@@ -45,2 +45,3 @@ "description": "React API connector",

"lint:types": "tsc --noEmit",
"prebuild": "yarn clean",
"build:cjs": "tsc",

@@ -47,0 +48,0 @@ "build:es": "tsc -m esNext --outDir esm",

<h1 align="center">React Aptor</h1>
<p align="center"><img src="./doc/assets/logo.svg" alt="react aptor logo" width="450"></p>
<p align="center">React API Connector</p>
<p align="center">Minimal API Connector for react</p>
<p align="center">

@@ -11,42 +11,61 @@ <a href="https://github.com/amirHossein-Ebrahimi/react-aptor/blob/master/license">

</a>
<a href="https://bundlephobia.com/result?p=react-aptor">
<img src="https://img.shields.io/bundlephobia/minzip/react-aptor.svg" alt="downloads" />
</a>
</a>
</p>
---
<p align="right">
<a href="./doc/localization/fa.md">آموزش فارسی</a>
</p>
Don’t waste your time by finding react version of your favorite javascript package, keep control of your `API` now.
## Why
Most packages are developed separately in javascript or typescript for increasing generality to make them us in all libraries and frameworks.
Most packages are developed separately in JavaScript for increasing generality being library/framework agnostic.
Connecting third parties to react is not a routine task. on the other hand, different teams might develop these packages hence development progress can be one step behind the original or terminated at any time.
Also, wrong abstraction or bad design patterns may slow down progress or block it at every new release.
Connecting vanilla third parties to react is not a routine task especially those that need to change the DOM.
On the other hand, these packages might be developed by different teams, hence development progress can be one step behind of the original or even be terminated at any time. Also, wrong abstraction or bad design patterns may interrupt the progress of these react`-something` packages.
List of some other concerns:
**Concerns**:
- Finding dom nodes by ReactDOM-findDOMNode
- Extensively usage of memoization to improve performance or prevent extra re-renders
- Large size of the project because of duplication and all API definition in react.
- Rely on a global scope (e.g. window) for package internal setting and making it impossible to have more than one instance.
- on finding DOM nodes by `ReactDOM.findDOMNode`
- Extensively usage of memorization to improve performance or prevent extra re-renders
- Other duplication layer for all API definition in react that increase the project size.
- Rely on a global scope (e.g. window) for internal setting (making it impossible to have more than one instance).
## react-aptor
We strive to solve them all at once
We strive to solve all mentioned problems at once and for all.
## Features
***
- **Small**
+ Zero-dependency with less than 1 kilobyte size 😱 [react-aptor](https://bundlephobia.com/result?p=react-aptor)
- **Manageable**
+ Your used/defined APIs are entirely under your control. Make it possible to define a slice of APIs which you are surely going to use.
- **React-ish**
+ Developed with lots of care, try to be zero-anti-pattern in react.
- **Simple**
- **Typescript**
<details>
<summary>Small</summary>
Zero-dependency with less than 1 kilobyte in size (327 B 😱) <a href="https://bundlephobia.com/result?p=react-aptor">react-aptor</a>
</details>
<details>
<summary>Manageable</summary>
Your used/defined APIs are entirely under your control. Make it possible to define a slice of APIs which you are surely going to use.
</details>
<details>
<summary>React-ish</summary>
Developed with lots of care, try to be zero-anti-pattern in react.
</details>
<details>
<summary>Simple</summary>
It's as simple as that. 💛
</details>
<details>
<summary>Typescript</summary>
Feel free to contribute or suggest any changes in <a href="https://github.com/amirHossein-Ebrahimi/react-aptor/issues">issues page</a>
</details>
## How to use
Connect your react app to any third party in three-step

@@ -56,32 +75,35 @@

2. Define the get API function
3. get connected to react by `useAptor`
3. Connect API to react by `useAptor`
---
1. **First step**
> Define the instantiate function.
1. Define the **instantiate** function.
```js
// construct.js
import Something from 'your-third-party'
import Something from 'some-third-party';
export default function instantiate(node, params) {
return new Something(node, {...params, ...yourCustomConfig} )
return new Something(node, params);
}
```
This function will return an instance of the third-party package. You have access to node (DOM-node) and params.
This function will return an instance of the third-party package. You have access to node (DOM-node\*) and params.
> The node is passed by react-aptor as a reference to DOM that is occasionally used as a wrapper for embedding UI.
> Params are optional parameters that are passed by react-aptor and define by you. see the third step.
> The `node` is passed by react-aptor as a reference to DOM that is occasionally used as a wrapper for embedding UI.
> The DOM-node\* will become more clear in the third step.
>
> The `params` are optional parameters that are passed by react-aptor and define by you. see the third step.
> The params will be passed by you and will be more clear in third step.
>
> name this file **construct.js** as convention ✨.
2. **Second step**
2. Define the **get API** function.
> Define the get API function.
```js
// api.js
export default function getAPI(instance, params) {
// return corresponding API Object
return () => ({
api_key: () => {
/* api defenition */
/* api definition using instance and params */
console.log(instance);
},

@@ -92,13 +114,18 @@ });

react-aptor will pass instance and params to your `getAPI` function. The instance is your third-party instance which has been defined in the first step.
The react-aptor will pass the latest instance of your third-party which has been defined in the first step by **instantiate** function along with **params** to **getAPI** function.
> Params are optional parameters that are passed by react-aptor and define by you. see the third step.
> The `instance` is returned instance of your third-party.
> Technically it is exactly going to be **instantiate(node, params)**
>
> The `params` are optional parameters that are passed by react-aptor and define by you. see the third step.
> The params will be passed by you and will be more clear in third step.
>
> name this file **api.js** as convention ✨.
3. **Third step**
3. **Connect API to react** by `useAptor`
```jsx
// connector.jsx
import useAptor from "react-aptor";
import getAPI from "./api";
import instantiate from "./construct";
import useAptor from 'react-aptor';
import getAPI from './api';
import instantiate from './construct';

@@ -118,9 +145,25 @@ const Connector = (props, ref) => {

For the connection phase, you need to define a `forwardRef` component, grab forwarded-ref and pass that as the first argument of`useAptor` hook. As 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`) with must be bound to your returned DOM node.
> name this file **connector.jsx** as convention ✨
> If you are using react 17 or newer version, you can also name it **connector.js**
The params will be then passed to your `instantiate` and `getAPI` function, as you saw in the first and second steps.
The value of params doesn't have any limitation, and it can be any arbitrary type (e.g. `undefined`, `number`, `string`, `object`). You have full access to props in your component and you can define params value by props too.
**useAptor in one look**
**Action Step**
```jsx
const aptorRef = useAptor(ref, configuration, deps);
```
**ref**
For 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.
**configuration**
As 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.
> 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.
> Params is the best place to connect props to your low-level api it means ”No Need” for extra function generation 🥳
**deps**
Is the same as Dependencies array default value is `[]` but you can override it as the third and lat argument of useAptor. It maybe needed in situation which you want to force re-instantiate by some prop change. It will use shallow comparison (as react do) for deps array and will call your `instantiate` & `getApI` in a row.
### API usage
```jsx

@@ -130,3 +173,7 @@ const Main = () => {

const apiKeyHandler = () => ref.current?.api_key();
const apiKeyHandler = () => {
if (ref.current) {
ref.current.api_key();
}
};

@@ -142,24 +189,151 @@ return (

Pass `createRef` to the Connector component (made in the third step), and then you can access all of the APIs inside `ref.current`
Pass **createRef** to the Connector component (made in the third step), and then you can access all of the APIs inside **ref.current**
## Full Typescript support
### 💡 Using of optional chaining
The project was developed by typescript, see samples for more info.
> function call can be much more readable with [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) & related [babel plugin](https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining)
## core
```jsx
const apiKeyHandler = () => ref.current?.api_key();
```
### Options
#### ref `ForwardedRef ref` *`required`*
The react `useRef` or `createRef` ref instance to store you api
#### configuration `Object` *`required`*
- ##### instantiate `function(node, params): Instance` *`required`*
A function that receives ref for probable bounded-node and params and returns an instance of your third-party.
- ##### getAPI `function(Instance, params): ApiObject` *`required`*
A function which receives instantiated instance and params and returns a key-value pair object for api handlers.
- ##### params `any`
Anything. It can be use props or pre-defined options.
### 💡 Better naming
#### deps `Array[any]` `[]`
react dependencies array for re-instantiating your third-party-packages by calling `instantiate` with latest node, params.
> In case you need `ref.current` more than one time, it is a good idea to rename it at the first place
```jsx
const apiKeyHandler = () => {
const { current: api } = ref; // store ref.current in `api`
if (api) {
api.api_key();
}
};
```
### 💡 Can I remove if check in handlers
Cause 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).
In normal world react will bind your API to given ref after the Connector mount
If you're using ref in useEffect or somewhere which is guaranteed to have the ref bounded to values, you can return proxy object in your getAPI function to bind all api functions simultaneously.
```js
export default function getAPI(thirdParty, params) {
if (!thirdParty)
return () =>
new Proxy(
{},
{
get: (_, prop) => {
// Possible to mock differently for different props
return noop;
},
}
);
return () => ({
api_key() {
// third-party is defined here for sure :)
console.log(thirdParty);
},
});
}
```
### 💡 Micro api instructions
> You can access all of you apis via `this` keyword
```js
export default function getAPI(sound, params) {
return () => ({
_state() {
return sound.getState();
},
play() {
if (this._state() === 'LOADED') sound.play();
},
});
}
```
> It's better to start name of this internal functions with `_`
### 💡 The `this` problem in API object
In a case you see this keyword usage in third-party API
you must specifying `this` something other than returned API object.
The following examples is for howler integration using react-aptor:
```js
{
// ❌ It doesn't work
state: howler.state,
// 🆗 this is Okay
state: howler.state.bind(howler),
// 🆗 this is also Okay
state: () => howler.state(),
// 🆗 this is also Okay, too
state() {
return howler.state();
}
}
```
### 💡 How to get API-Object type
You can use something like the follwing:
```ts
export type APITypes = ReturnType<ReturnType<typeof getAPI>>;
```
### 💡 How to make a custom react integeration packge using react-aptor
1. Name you packge **raptor-something** :)
2. Use minimum possible configuration for your api
3. Interact to props change in your component using `useEffect` and proper `deps` array
4. Bind another ref to you Connector using fork-ref idea, for other possible usage
See all in action in **[raptor-howler-example](https://github.com/amirHossein-Ebrahimi/raptor-howler/tree/master/example)**
## core
### **ref** _`required`_
The react **useRef** or **createRef** ref instance which has been passed throw **react.forwardRef** method.
your api will be stored in this ref.
### **configuration** _`required`_
- ### **instantiate** _`required`_
> function(node, params): Instance
A function that receives probable bounded-node and params. It then returns an instance of your third-party.
- ### **destroy**
> function(previousInstance, params): void
A function that receives previous created instance 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** & etc
- ### **getAPI** _`required`_
> function(Instance, params): ApiObject
A function which receives instance of you third-party and params. It then returns a key-value pair object for api handlers.
- ### **params** `any`
Params can have any arbitrary type and can be used with props or pre-defined options.
### **deps** `[]`
React dependencies array for re-instantiating your third-party-packages. It will call `instantiate` with latest node, params when ever shallow comparison for with the previous deps array finds inequality.
---
## **Donation**

@@ -173,3 +347,2 @@

## Samples

@@ -179,29 +352,22 @@

> Quill is a free, open source WYSIWYG editor built for the modern web. <img src="https://img.shields.io/github/stars/quilljs/quill?style=social" />
> <a href="https://codesandbox.io/s/react-aptor--quill-iqwcd"><img src="https://codesandbox.io/static/img/play-codesandbox.svg"></a>
> Quill is a free, open source WYSIWYG editor built for the modern web. <img src="https://img.shields.io/github/stars/quilljs/quill?style=social" /> > <a href="https://codesandbox.io/s/react-aptor--quill-iqwcd"><img src="https://codesandbox.io/static/img/play-codesandbox.svg"></a>
### [Fabric.js](http://fabricjs.com)
> Fabric.js is a powerful and simple. Javascript HTML5 canvas library. <img src="https://img.shields.io/github/stars/fabricjs/fabric.js?style=social" />
> <a href="https://codesandbox.io/s/react-aptor--fabric-hp50c"><img src="https://codesandbox.io/static/img/play-codesandbox.svg"></a>
> Fabric.js is a powerful and simple. Javascript HTML5 canvas library. <img src="https://img.shields.io/github/stars/fabricjs/fabric.js?style=social" /> > <a href="https://codesandbox.io/s/react-aptor--fabric-hp50c"><img src="https://codesandbox.io/static/img/play-codesandbox.svg"></a>
### [Rive.js](https://rive.app)
> Create and ship beautiful animations to any platform. <img src="https://img.shields.io/github/stars/rive-app/rive-wasm?style=social" />
> <a href="https://stackblitz.com/edit/react-aptor-rivejs"><img width="165" src="https://developer.stackblitz.com/img/logo.svg"></a>
> Create and ship beautiful animations to any platform. <img src="https://img.shields.io/github/stars/rive-app/rive-wasm?style=social" /> > <a href="https://stackblitz.com/edit/react-aptor-rivejs"><img width="165" src="https://developer.stackblitz.com/img/logo.svg"></a>
### [Howler.js](https://howlerjs.com)
### [Howler.js](https://howlerjs.com)
> Audio library for the modern web. <img src="https://img.shields.io/github/stars/goldfire/howler.js?style=social" /> > <a href="https://codesandbox.io/s/react-aptor--howler-4o8t4"><img src="https://codesandbox.io/static/img/play-codesandbox.svg"></a>
> Audio library for the modern web. <img src="https://img.shields.io/github/stars/goldfire/howler.js?style=social" />
> <a href="https://codesandbox.io/s/react-aptor--howler-4o8t4"><img src="https://codesandbox.io/static/img/play-codesandbox.svg"></a>
### [Reveal.js](https://revealjs.com)
### [Reveal.js](https://revealjs.com)
> HTML presentation framework create fully featured and beautiful presentations. <img src="https://img.shields.io/github/stars/hakimel/reveal.js?style=social" /> > <a href="https://codesandbox.io/s/react-aptor--reveal-dwrke"><img src="https://codesandbox.io/static/img/play-codesandbox.svg"></a>
> HTML presentation framework create fully featured and beautiful presentations. <img src="https://img.shields.io/github/stars/hakimel/reveal.js?style=social" />
> <a href="https://codesandbox.io/s/react-aptor--reveal-dwrke"><img src="https://codesandbox.io/static/img/play-codesandbox.svg"></a>
<p align="center">
<a href="./doc/samples.md"> ⭐ SEE ALL SAMPLES 🌟</a>
</p>

@@ -1,10 +0,2 @@

import {
useEffect,
useImperativeHandle,
useState,
useRef,
ForwardedRef,
RefObject,
useMemo,
} from 'react';
import { useEffect, useImperativeHandle, useState, useRef, RefObject, useMemo, Ref } from 'react';

@@ -14,4 +6,3 @@ // types:misc

// types:api
export type SingleAPI = (...args: any[]) => any;
export type APIObject = { [apiName: string]: SingleAPI };
export type APIObject = Record<string, any>; // function, class, ... as api-value
export type APIGenerator = () => APIObject;

@@ -21,5 +12,8 @@ export type GetAPI<T> = (instance: Nullable<T>, prams?: any) => APIGenerator;

export type Instantiate<T> = (node: Nullable<HTMLElement>, params?: any) => Nullable<T>;
export type Destroy<T> = (instance: Nullable<T>, params?: any) => void;
export interface AptorConfiguration<T> {
getAPI: GetAPI<T>;
instantiate: Instantiate<T>;
destroy: Destroy<T>;
params?: any;

@@ -36,12 +30,16 @@ }

export default function useAptor<T>(
ref: ForwardedRef<APIObject>,
ref: Ref<APIObject>,
configuration: AptorConfiguration<T>,
deps = []
deps: any[] = []
): RefObject<HTMLElement> {
const [instance, setInstance] = useState<Nullable<T>>(null);
const domRef = useRef<Nullable<HTMLElement>>(null);
const { instantiate, getAPI, params } = configuration;
const { instantiate, destroy, getAPI, params } = configuration;
useEffect(() => {
setInstance(instantiate(domRef.current, params));
const instanceReference = instantiate(domRef.current, params);
setInstance(instanceReference);
return () => {
if (destroy) destroy(instanceReference, params);
};
// eslint-disable-next-line react-hooks/exhaustive-deps

@@ -48,0 +46,0 @@ }, deps);

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc