{"id":21696697,"url":"https://github.com/orlov-vo/ramodel","last_synced_at":"2025-04-12T12:11:41.017Z","repository":{"id":42568186,"uuid":"246497360","full_name":"orlov-vo/ramodel","owner":"orlov-vo","description":"☀️🦉Create reactive \u0026 flexible models with Hooks API.","archived":false,"fork":false,"pushed_at":"2024-12-15T00:50:27.000Z","size":1057,"stargazers_count":3,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-26T06:51:11.523Z","etag":null,"topics":["effects","flux","hooks-api","model","state-management","typescript","webextension","webworker"],"latest_commit_sha":null,"homepage":"","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/orlov-vo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-11T06:56:10.000Z","updated_at":"2022-02-15T09:39:28.000Z","dependencies_parsed_at":"2024-11-25T19:22:05.017Z","dependency_job_id":"08197808-3f03-43f1-8ba3-8deccaf760c7","html_url":"https://github.com/orlov-vo/ramodel","commit_stats":{"total_commits":108,"total_committers":2,"mean_commits":54.0,"dds":"0.11111111111111116","last_synced_commit":"cd81de59f1aa4593cc2aba297c6d4001afaa2c12"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orlov-vo%2Framodel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orlov-vo%2Framodel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orlov-vo%2Framodel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/orlov-vo%2Framodel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/orlov-vo","download_url":"https://codeload.github.com/orlov-vo/ramodel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248565074,"owners_count":21125417,"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":["effects","flux","hooks-api","model","state-management","typescript","webextension","webworker"],"created_at":"2024-11-25T19:21:24.834Z","updated_at":"2025-04-12T12:11:40.990Z","avatar_url":"https://github.com/orlov-vo.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RaModel\n\n\u003cimg src=\"https://orlov-vo.github.io/ramodel/logotype.svg\" align=\"right\"\n     alt=\"RaModel logotype\" width=\"200\" height=\"200\"\u003e\n\nA library for creating reactive \u0026 flexible models with Hooks API.\nCan works together with [**React**](#react-and-preact), [**Preact**](#react-and-preact) and [**Svelte**](#svelte).\n\n[API References](#api-references) •\n[Demo TodoMVC example](https://github.com/orlov-vo/ramodel-demo-todomvc)\n\n- **Hooks.** It has Hooks like it made in [React](https://reactjs.org/docs/hooks-reference.html).\n- **Fast**. It has many performance optimization to track changes only from needed instances.\n- **Typed.** The library provide full coverage typings via TypeScript.\n- **Contexts.** It has painless API to communicate with another execution contexts (eg. WebWorker, Node.js).\n- **Small.** We try to minimize distributed size and use tiny dependencies.\n\n[![Version](https://img.shields.io/npm/v/ramodel)](https://www.npmjs.com/package/ramodel)\n[![Size on bundlephobia](https://img.shields.io/bundlephobia/minzip/ramodel)](https://bundlephobia.com/result?p=ramodel)\n[![Openned issues](https://img.shields.io/github/issues-raw/orlov-vo/ramodel)](https://github.com/orlov-vo/ramodel/issues)\n[![License MIT](https://img.shields.io/npm/l/ramodel)](https://github.com/orlov-vo/ramodel/blob/master/LICENSE.md)\n\n## Quick Start\n\n### 1. Install the library from NPM\n\nExecute this command in your project to install the library as new dependency:\n\n```sh\nnpm install --save ramodel\n```\n\nOr if you using yarn:\n\n```sh\nyarn add ramodel\n```\n\n### 2. Define your first model.\n\nImport [`createModel`](#createmodel) method from the library\n\n```js\nimport { createModel } from 'ramodel';\n```\n\nIf you are using TypeScript you can write type for the model's input. This type will be used when you construct new instance of model or update the instance's input via [`update`](#update) method.\n\n```ts\ninterface CarProps {\n  fuelQuantity: number;\n  fuelConsumption: number;\n}\n```\n\nNow you can define your model with [`createModel`](#createmodel):\n\n```ts\nconst Car = createModel((props: CarProps) =\u003e {\n  // Main body which re-run on every update\n  // Here you can use Hooks from `ramodel/hooks` module\n  ...\n\n  return {\n    // Public methods and properties\n    ...\n  };\n});\n```\n\nThis is very similar to the React's functional components, but instead of returning elements, in RaModel you should return public methods and properties. Important: they are read-only, it helps you to use them without unpredictable state mutating from outside. You can mutate the model's state only with public methods\n\nAfter defining you can construct new instance via `new` operator:\n\n```js\nconst jeep = new Car({\n  fuelQuantity: 40,\n  fuelConsumption: 1,\n});\n```\n\nThe `jeep` variable will have all defined public methods and properties before in `Car` model.\n\nMain the library's limitation is instances should be destroyed manually via [`destroy`](#destroy) method:\n\n```js\ndestroy(jeep);\n```\n\n### 3. Subscribe to instance's updates\n\nTo watch needed updates you can use the system of lenses. It is power functional concept let us get the whole information about using models in result of calculation. This information can be used in [`watch`](#watch) method:\n\n```js\nimport { createLens, watch } from 'ramodel';\n\nconst odometerLens = createLens(motocycle, _ =\u003e _.odometer);\n\nwatch(odometerLens, odometer =\u003e console.log(`Motocycle's odometer: ${odometer}`));\n// or you can use alias:\nodometerLens.subscribe(odometer =\u003e console.log(`Motocycle's odometer: ${odometer}`));\n```\n\nTo debug state's changes you can use [`createLogger`](#createlogger) or [`connectReduxDevtools`](#connectreduxdevtools) method from devtools:\n\n```js\nimport { createLogger } from 'ramodel/devtools';\n\ncreateLogger(motocycle);\n// or\nconnectReduxDevtools(motocycle);\n```\n\n### 4. Advance using models from remote context\n\nAll power of RaModel in providing simple API to use reactive models from remote contexts. The remote context here may be Web Worker or `window` from extension's background page or some remote server.\n\nFor example if you has some `worker.js` script which will be executed in Web Worker contexts you can export your instances to the main process.\n\n```js\n// worker.js - Worker's process\nimport { expose } from 'ramodel/remote/worker';\n\nconst world = expose();\nworld.set('jeep', jeep);\n```\n\nAn then in main process you can import the `jeep` instance:\n\n```js\n// main.js - Main process\nimport { connect } from 'ramodel/remote/worker';\n\nconst worker = new Worker('worker.js', { type: 'module' });\nconst remoteWorld = connect(worker);\n\nconst jeep = await remoteWorld.get('jeep');\n\n// Now you can use `jeep` like local model\n// But it continue live in the worker process\n```\n\n## Integration with popular frameworks\n\nRaModel has very simple API which easy to ingrate with many popular library and frameworks.\nMost often you only need to use lenses and a [`watch`](#watch) method for subscribe updates.\n\n### React and Preact\n\n```js\nimport { useLens } from 'ramodel/react'; // or from 'ramodel/preact'\nimport { odometerLens } from './lenses';\nimport { driver } from './instances';\n\nfunction App() {\n  const odometer = useLens(odometerLens);\n  const driverName = useLens(driver, _ =\u003e _.name);\n\n  return (\n    \u003cdiv\u003e\n      {driverName}: {odometer} miles\n    \u003c/div\u003e\n  );\n}\n```\n\n### Svelte\n\nAll lenses are fully compliant [Store contract](https://svelte.dev/docs#Store_contract) in read-only mode.\nBecause of this you can use lenses as [reactive variables](https://svelte.dev/docs#4_Prefix_stores_with_$_to_access_their_values).\n\n```html\n\u003cscript\u003e\n  import { odometerLens } from './lenses';\n\u003c/script\u003e\n\n\u003cdiv\u003e{$odometerLens} miles\u003c/div\u003e\n```\n\n### Angular, Vue and other frameworks\n\nSorry but I don't know these frameworks deeply to write good integration with them, if you want help me just [create a new issue](https://github.com/orlov-vo/ramodel/issues/new).\n\n## API References\n\n- `ramodel`\n  - Model\n    - [`createModel`](#createmodel)\n    - [`update`](#update)\n    - [`destroy`](#destroy)\n  - Lenses\n    - [`createLens`](#createlens)\n    - [`combineLenses`](#combinelenses)\n    - [`isLens`](#islens)\n    - [`watch`](#watch)\n  - Helpers\n    - [`createContext`](#createcontext)\n    - [`get`](#get)\n- `ramodel/hooks`\n  - Basic Hooks\n    - [`useState`](#usestate)\n    - [`useEffect`](#useeffect)\n  - Additional Hooks\n    - [`useReducer`](#usereducer)\n    - [`useMemo`](#usememo)\n    - [`useCallback`](#usecallback)\n    - [`useRef`](#useref)\n  - RaModel Hooks\n    - [`useLens`](#uselens)\n    - [`useModel`](#usemodel)\n    - [`useModelFabric`](#usemodelfabric)\n- `ramodel/remote/generic`\n  - [`connect`](#generic-connect)\n  - [`expose`](#generic-expose)\n- `ramodel/remote/worker`\n  - [`connect`](#worker-connect)\n  - [`expose`](#worker-expose)\n- `ramodel/remote/global`\n  - [`connect`](#global-connect)\n  - [`expose`](#global-expose)\n- `ramodel/remote/local-storage`\n  - [`connect`](#local-storage-connect)\n  - [`expose`](#local-storage-expose)\n- `ramodel/remote/chrome`\n  - [`connect`](#chrome-connect)\n  - [`expose`](#chrome-expose)\n- `ramodel/devtools`\n  - [`createLogger`](#createlogger)\n  - [`connectReduxDevtools`](#connectreduxdevtools)\n\n### `createModel`\n\n```js\nimport { createModel } from 'ramodel';\n\nconst Model = createModel(mainFn);\n```\n\nCreate a new model which will use a passed function. This function can use [Hooks](api-hooks.md) and returns public methods and properties. Returns class for creating a model instance\n\n```js\nconst Model = createModel((props) =\u003e {\n  // Main body which re-run on every update\n  // Here you can use Hooks\n  ...\n\n  return {\n    // Public methods and properties\n    ...\n  };\n});\n\n// Create an instance of Model with passed props\nconst modelInstance = new Model(props);\n```\n\nIf you want use reference to class as type you can use another way of defining models via `class extends`:\n\n```ts\nclass Model extends createModel(() =\u003e {\n  /* ... */\n}) {}\n```\n\nIf you want skip creating model and fast create needed instance you can use this shortcut:\n\n```js\nimport { createInstance } from 'ramodel';\n\nconst modelInstance = createInstance(input, mainFn);\n```\n\n### `update`\n\n```js\nimport { update } from 'ramodel';\n\nupdate(modelInstance, newInput);\n```\n\nUpdate input in the model and re-run main function\n\n```js\nconst User = createModel(({ firstName, lastName }) =\u003e {\n  return { name: `${firstName} ${lastName}` };\n});\n\nconst john = new User({ firstName: 'John', lastName: 'Doe' });\nconsole.log(john.name); // =\u003e 'John Doe'\n\n// Update input and re-run main function in model\nupdate(john, { firstName: 'Jesica', lastName: 'Brown' });\nconsole.log(john.name); // =\u003e 'Jesica Brown'\n```\n\n### `destroy`\n\n```js\nimport { destroy } from 'ramodel';\n\ndestroy(...modelInstances);\n```\n\nShutdown all side effects and clean the state in models instances\n\n### `createLens`\n\n```js\nimport { createLens } from 'ramodel';\n\nconst lens = createLens(modelInstance, accessorFunction);\n```\n\nCreate lens. Works like [`get()`](api-main.md#get) but returns lens instead value\n\n### `combineLenses`\n\n```js\nimport { combineLenses } from 'ramodel';\n\nconst lens = combineLenses(lenses, handler);\n```\n\nCombine lenses in the one. It is very handly when you need to calculate value which depends on multiple lenses.\n\n### `isLens`\n\n```js\nimport { isLens } from 'ramodel';\n\nif (isLens(maybeLens)) {\n  // `maybeLens` is definitely lens here\n}\n```\n\nYou can check your value that this is exactly a lens with `isLens` method.\n\n### `watch`\n\n```js\nimport { watch } from 'ramodel';\n\nconst unsubscribe = watch(lenses, handler);\n```\n\nWatch for changes in models use lenses. The `handler` recive values extracted with `accessorFunction`. Returns function for unsubscribe\n\n```js\nwatch([lens], value =\u003e {\n  console.log(value);\n});\n```\n\n### `createContext`\n\n```js\nimport { createContext } from 'ramodel';\n\nconst Context = createContext(defaultValue);\n```\n\nCreates a Context object.\n\nThe `defaultValue` argument is only used when a model does not have a matching Context's value above it in the tree. This can be helpful for testing models in isolation without wrapping them. Note: passing `undefined` as a Context's value does not cause consuming models to use `defaultValue`.\n\nFor provide value you can use `Context.withValue`:\n\n```js\nContext.withValue(newValue, () =\u003e {\n  // here you can create new model's instances\n  // they will get the `newValue` when we `useContext` hook\n});\n```\n\nYou can dynamicly update the Context's value in instance and its children tree with `Context.updateValue`:\n\n```js\nContext.updateValue(instance, newValue);\n```\n\nAlso you can delete the Context from instance with `Context.removeFrom`:\n\n```js\nContext.removeFrom(instance);\n```\n\n### `get`\n\n```js\nimport { get } from 'ramodel';\n\nconst value = get(object, accessorFunction);\n```\n\nTraverses properties on objects and arrays. If an intermediate property is either null or undefined, it is instead returned. The purpose of this method is to simplify extracting properties from a chain of maybe-typed properties.\n\nReturns the property accessed if accessor function could reach to property, null or undefined otherwise\n\nConsider the following type:\n\n```js\nconst props: {\n  user?: {\n    name: string,\n    friends?: Array\u003cUser\u003e,\n  }\n};\n```\n\nGetting to the friends of my first friend would resemble:\n\n```js\nprops.user \u0026\u0026 props.user.friends \u0026\u0026 props.user.friends[0] \u0026\u0026 props.user.friends[0].friends;\n```\n\nInstead, `get` allows us to safely write:\n\n```js\nget(props, _ =\u003e _.user.friends[0].friends);\n```\n\nThe second argument must be a function that returns one or more nested member expressions. Any other expression has undefined behavior.\n\n### `useState`\n\n```js\nimport { useState } from 'ramodel/hooks';\n\nconst [state, setState] = useState(initialState);\n```\n\nReturns a stateful value, and a function to update it.\n\nDuring the initialization, the returned state (`state`) is the same as the value passed as the first argument (`initialState`).\n\nThe `setState` function is used to update the state. It accepts a new state value and enqueues a re-run main function of the model.\n\n```js\nsetState(newState);\n```\n\nDuring subsequent re-updates, the first value returned by `useState` will always be the most recent state after applying updates.\n\nHere’s the simple counter example:\n\n```js\nconst Counter = createModel(() =\u003e {\n  const [count, setCount] = useState(0);\n  return { count, setCount };\n});\n\nconst counter = new Counter();\n\nconsole.log(counter.count); // =\u003e 0\ncounter.setState(5);\nconsole.log(counter.count); // =\u003e 5\ncounter.setState(count =\u003e count - 2);\nconsole.log(counter.count); // =\u003e 3\n```\n\n`setState` returns a promise which resolves when instance is updated before effects runs. It can be useful if you need wait model updating.\nIn main cases if you work with same instance it can be not needed because on every direct reading we try to finish all planned tasks for the instance before reading if it possible.\n\n### `useEffect`\n\n```js\nimport { useEffect } from 'ramodel/hooks';\n\nuseEffect(didUpdate);\n```\n\nAccepts a function that contains imperative, possibly effectful code.\n\nMutations, subscriptions, timers, logging, and other side effects are not allowed inside the main body of a model. Doing so will lead to confusing bugs and inconsistencies in the state\n\nInstead, use `useEffect`. The function passed to `useEffect` will run after a changes is committed to the model. Think of effects as an escape hatch from purely functional world into the imperative world.\n\nBy default, effects run after every completed change, but you can choose to fire them only when certain values have changed.\n\n#### Cleaning up an effect\n\nOften, effects create resources that need to be cleaned up before the model will be destroyed, such as a subscription or timer ID. To do this, the function passed to `useEffect` may return a clean-up function. For example, to create a subscription:\n\n```js\nuseEffect(() =\u003e {\n  const subscription = props.source.subscribe();\n\n  return () =\u003e {\n    // Clean up the subscription\n    subscription.unsubscribe();\n  };\n});\n```\n\nThe clean-up function runs when the model passed into [`destroy`](api-main.md#destroy) function to prevent memory leaks. Additionally, if a model changes multiple times (as they typically do), the previous effect is cleaned up before executing the next effect. In our example, this means a new subscription is created on every update. To avoid firing an effect on every update, refer to the next section.\n\n#### Conditionally firing an effect\n\nThe default behavior for effects is to fire the effect after every completed change. That way an effect is always recreated if one of its dependencies changes.\n\nHowever, this may be overkill in some cases, like the subscription example from the previous section. We don’t need to create a new subscription on every update, only if the source prop has changed.\n\nTo implement this, pass a second argument to `useEffect` that is the array of values that the effect depends on. Our updated example now looks like this:\n\n```js\nuseEffect(() =\u003e {\n  const subscription = props.source.subscribe();\n\n  return () =\u003e {\n    subscription.unsubscribe();\n  };\n}, [props.source]);\n```\n\nNow the subscription will only be recreated when props.source changes.\n\nThe array of dependencies is not passed as arguments to the effect function. Conceptually, though, that’s what they represent: every value referenced inside the effect function should also appear in the dependencies array.\n\n### `useReducer`\n\n```js\nimport { useReducer } from 'ramodel/hooks';\n\nconst [state, dispatch] = useReducer(reducer, initialArg, init);\n```\n\nAn alternative to [`useState`](#usestate). Accepts a reducer of type `(state, action) =\u003e newState`, and returns the current state paired with a `dispatch` method. (If you’re familiar with Redux, you already know how this works.)\n\n`useReducer` is usually preferable to [`useState`](#usestate) when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. `useReducer` also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.\n\nHere’s the counter example from the [`useState` section](#usestate), rewritten to use a reducer:\n\n```js\nconst initialState = { count: 0 };\n\nfunction reducer(state, action) {\n  switch (action.type) {\n    case 'increment':\n      return { count: state.count + 1 };\n    case 'decrement':\n      return { count: state.count - 1 };\n    default:\n      throw new Error();\n  }\n}\n\nconst Counter = createModel(() =\u003e {\n  const [{ count }, dispatch] = useReducer(reducer, initialState);\n\n  return {\n    count,\n    decrement: () =\u003e dispatch({ type: 'decrement' }),\n    increment: () =\u003e dispatch({ type: 'increment' }),\n  };\n});\n\nconst counter = new Counter();\n\nconsole.log(counter.count); // =\u003e 0\ncounter.increment();\nconsole.log(counter.count); // =\u003e 1\ncounter.decrement();\nconsole.log(counter.count); // =\u003e 0\n```\n\n`dispatch` returns a promise which resolves when instance is updated before effects runs. It can be useful if you need wait model updating.\nIn main cases if you work with same instance it can be not needed because on every direct reading we try to finish all planned tasks for the instance before reading if it possible.\n\n#### Specifying the initial state\n\nThere are two different ways to initialize `useReducer` state. You may choose either one depending on the use case. The simplest way is to pass the initial state as a second argument:\n\n```js\nconst [state, dispatch] = useReducer(reducer, { count: initialCount });\n```\n\n#### Lazy initialization\n\nYou can also create the initial state lazily. To do this, you can pass an `init` function as the third argument. The initial state will be set to `init(initialArg)`:\n\n```js\nfunction init(count) {\n  return { count };\n}\n\nconst [state, dispatch] = useReducer(reducer, initialCount, init);\n```\n\n### `useMemo`\n\n```js\nimport { useMemo } from 'ramodel/hooks';\n\nconst memoizedValue = useMemo(() =\u003e computeExpensiveValue(a, b), [a, b]);\n```\n\nReturns a memoized value.\n\nPass a “create” function and an array of dependencies. `useMemo` will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every update.\n\nIf no array is provided, a new value will be computed on every update.\n\n**You may rely on `useMemo` as a performance optimization, not as a semantic guarantee**. Write your code so that it still works without `useMemo` — and then add it to optimize performance.\n\n### `useCallback`\n\n```js\nimport { useCallback } from 'ramodel/hooks';\n\nconst memoizedCallback = useCallback(() =\u003e {\n  doSomething(a, b);\n}, [a, b]);\n```\n\nReturns a memoized callback.\n\nPass an inline callback and an array of dependencies. `useCallback` will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child models that rely on reference equality to prevent unnecessary updates.\n\n`useCallback(fn, deps)` is equivalent to `useMemo(() =\u003e fn, deps)`.\n\n### `useRef`\n\n```js\nimport { useRef } from 'ramodel/hooks';\n\nconst refContainer = useRef(initialValue);\n```\n\n`useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument (`initialValue`). The returned object will persist for the full lifetime of the component.\n\nEssentially, `useRef` is like a “box” that can hold a mutable value in its `.current` property.\n\nIt’s handy for keeping any mutable value around similar to how you’d use instance fields in classes:\n\n```js\nconst FormObserver = createModel(({ subscribe }) =\u003e {\n  // We use `useRef` here instead `useState` because nobody depends on that state\n  // and we don't want to re-run main function on receive new form\n  const form = useRef(null);\n\n  useEffect(\n    () =\u003e\n      subscribe(newForm =\u003e {\n        form.current = newForm;\n      }),\n    [subscribe],\n  );\n\n  return {\n    focus: () =\u003e {\n      if (form.current) {\n        form.current.focus();\n      }\n    },\n  };\n});\n```\n\nThis works because `useRef()` creates a plain JavaScript object. The only difference between `useRef()` and creating a `{current: ...}` object yourself is that useRef will give you the same ref object on every update.\n\nKeep in mind that `useRef` doesn’t notify you when its content changes. Mutating the `.current` property doesn’t cause a re-update like `useState`.\n\n### `useLens`\n\n```js\nimport { useLens } from 'ramodel/hooks';\n\nconst value = useLens(lens);\n// or you can embed lens creating:\nconst value = useLens(instance, _ =\u003e _.value);\n```\n\nWith `useLens` hook you can get current value from the lens (which can created with [`createLens`](#createlens) or [`combineLenses`](#combinelenses)) and all subsequent values ​​in updates. Also you can embed lens creating to this hook if you pass arguments to this hook as for [`createLens`](#createlens) method.\n\n### `useModel`\n\n```js\nimport { useModel } from 'ramodel/hooks';\n\nconst child = useModel(Model, {\n  input: {},\n  contexts: [\n    [ContextA, contextValueA],\n    [ContextB, contextValueB],\n  ],\n});\n```\n\n### `useModelFabric`\n\n```js\nimport { useModelFabric } from 'ramodel/hooks';\n\nconst createInstance = useModelFabric(Model, {\n  // Here you can pass list of pairs of context and value\n  // It supports dynamic updates of values for new and all previously created instances\n  contexts: [\n    [FooContext, 'foo'],\n    [BarContext, 'bar'],\n  ],\n});\n\nconst instance = createInstance({ foo: 'bar' });\n```\n\n### Generic `connect`\n\n```js\nimport { connect } from 'ramodel/generic/generic';\n\nconst clientId = 'website';\nconst webSocket = new WebSocketChannel();\nconst remoteWorld = connect({\n  postMessage: msg =\u003e sendMessage(msg, { clientId }),\n  onInit: ({ onMessage }) =\u003e {\n    webSocket.listen('message', data =\u003e {\n      const { message, ...meta } = JSON.parse(data);\n      if (clientId !== meta.clientId) return;\n\n      onMessage(message);\n    });\n  },\n});\n\nconst myRemoteModel = await remoteWorld.get('my-model');\n\n// Now you can use `myRemoteModel` like local model\n// But it continue live in the background page's process\n```\n\n### Generic `expose`\n\n```js\nimport { expose } from 'ramodel/remote/generic';\n\nconst webSocket = new WebSocketChannel();\nconst world = expose({\n  onInit: ({ onMessage }) =\u003e {\n    webSocket.listen('message', data =\u003e {\n      const { message, ...meta } = JSON.parse(data);\n      if (!meta.clientId) return;\n\n      onMessage(message, data =\u003e sendMessage(key, data, { clientId: meta.clientId }));\n    });\n  },\n});\n\nworld.set('my-model', myLocalModel);\n```\n\n### Worker `connect`\n\n```js\nimport { connect } from 'ramodel/remote/worker';\n\nconst worker = new Worker('worker.js', { type: 'module' });\nconst remoteWorld = connect(worker);\n\nconst myRemoteModel = await remoteWorld.get('my-model');\n\n// Now you can use `myRemoteModel` like local model\n// But it continue live in the worker process\n```\n\n### Worker `expose`\n\n```js\n// Worker's process\nimport { expose } from 'ramodel/remote/worker';\n\nconst world = expose();\nworld.set('my-model', myLocalModel);\n```\n\n### Global `connect`\n\n```js\nimport { connect } from 'ramodel/remote/global';\n\nconst backgroundWindow = chrome.extension.getBackgroundPage();\nconst remoteWorld = connect(backgroundWindow);\n\nconst myRemoteModel = await remoteWorld.get('my-model');\n\n// Now you can use `myRemoteModel` like local model\n// But it continue live in the background page's process\n```\n\n### Global `expose`\n\n```js\nimport { expose } from 'ramodel/remote/global';\n\nconst backgroundWindow = chrome.extension.getBackgroundPage();\nconst world = expose(backgroundWindow);\nworld.set('my-model', myLocalModel);\n```\n\n### LocalStorage `connect`\n\n```js\nimport { connect } from 'ramodel/remote/local-storage';\n\nconst remoteWorld = connect();\nconst myRemoteModel = await remoteWorld.get('my-model');\n\n// Now you can use `myRemoteModel` like local model\n// But it continue live in the background page's process\n```\n\n### LocalStorage `expose`\n\n```js\nimport { expose } from 'ramodel/remote/local-storage';\n\nconst world = expose();\nworld.set('my-model', myLocalModel);\n```\n\n### Chrome `connect`\n\n```js\nimport { connect } from 'ramodel/remote/chrome';\n\nconst remoteWorld = connect();\nconst myRemoteModel = await remoteWorld.get('my-model');\n\n// Now you can use `myRemoteModel` like local model\n// But it continue live in the background page's process\n```\n\n### Chrome `expose`\n\n```js\nimport { expose } from 'ramodel/remote/chrome';\n\nconst world = expose();\nworld.set('my-model', myLocalModel);\n```\n\n### `createLogger`\n\n```js\nimport { createLogger } from 'ramodel/devtools';\n\ncreateLogger(instance, {\n  name: 'my awesome instance',\n  diff: true,\n});\n```\n\n### `connectReduxDevtools`\n\n```js\nimport { connectReduxDevtools } from 'ramodel/devtools';\n\nconnectReduxDevtools(instance, { name: 'my awesome instance' });\n```\n\nYou can connect [Redux DevTools](https://github.com/reduxjs/redux-devtools) to debug your model instance.\nIt has a very basic integration with it, some features may not worked.\n\n## Thanks\n\nThis project based on [source code of \"haunted\"](https://github.com/matthewp/haunted).\n\n- Thanks [Gleb Arestov](https://github.com/arestov) and him project [Deklarota](https://github.com/arestov/deklarota) for inspire me to create this library for reactive model management.\n- Thanks [Matthew Phillips](https://github.com/matthewp) and other contributors for their big work under re-implementing Hooks API.\n- Also big thanks to React's documentation authors for their very clear documentation about Hooks conceptions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forlov-vo%2Framodel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Forlov-vo%2Framodel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forlov-vo%2Framodel/lists"}