{"id":50840400,"url":"https://github.com/patrickjames242/react-state-object","last_synced_at":"2026-06-14T06:08:36.356Z","repository":{"id":340222474,"uuid":"1165058047","full_name":"patrickjames242/react-state-object","owner":"patrickjames242","description":"A small library for building class-based React state that is designed to work primarily with MobX.","archived":false,"fork":false,"pushed_at":"2026-03-28T23:01:36.000Z","size":440,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-29T00:09:28.776Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/patrickjames242.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-23T19:27:17.000Z","updated_at":"2026-03-28T23:01:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/patrickjames242/react-state-object","commit_stats":null,"previous_names":["patrickjames242/react-state-object"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/patrickjames242/react-state-object","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickjames242%2Freact-state-object","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickjames242%2Freact-state-object/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickjames242%2Freact-state-object/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickjames242%2Freact-state-object/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/patrickjames242","download_url":"https://codeload.github.com/patrickjames242/react-state-object/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickjames242%2Freact-state-object/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34310844,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2026-06-14T06:08:35.477Z","updated_at":"2026-06-14T06:08:36.340Z","avatar_url":"https://github.com/patrickjames242.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-state-object\n\n`react-state-object` is a small library for building\nclass-based React state that is designed to work\nprimarily with **MobX**.\n\nIt gives you:\n\n- a base class (`ReactStateObject`) with mount/unmount\n  lifecycle hooks\n- automatic lifecycle propagation through nested child\n  state objects\n- a way to use React hooks inside class accessors\n  (`@fromHook(...)`)\n- a simple class-instance injection system for React\n  trees\n- a convenience decorator for injected state accessors\n  (`@injectInstance(...)`)\n\nIf you are new to MobX, this README teaches the\nbasics first and then shows how `react-state-object`\nfits on top.\n\n## Who This Is For\n\nThis library is a good fit when you:\n\n- like class-based state models\n- use MobX for observability/computed values/actions\n- want React lifecycle ownership (`mount/unmount`) for\n  those classes\n- want to inject shared state instances without wiring\n  dozens of props\n\nThis library is **not** a replacement for MobX. It is a\nsmall layer that helps MobX state objects live inside a\nReact app in a structured way.\n\n## Install\n\n```bash\nnpm install react-state-object mobx react\n```\n\nYou will usually also want:\n\n```bash\nnpm install mobx-react-lite\n```\n\n`mobx-react-lite` is not a peer dependency of this\npackage, but it is the most common way to make React\ncomponents re-render when MobX observables change.\n\n## Peer Dependencies\n\n- `react` `\u003e=18`\n- `mobx` `\u003e=6.0.0 \u003c7`\n\n## What MobX Does (Beginner-Friendly)\n\nMobX is a state management library built around a simple\nidea:\n\n- mark values as **observable**\n- derive values with **computed** getters\n- change values in **action** methods\n- make React components **observer(...)** so they\n  re-render when observables they read change\n\n### Tiny MobX example (without `react-state-object` yet)\n\n```tsx\nimport { action, computed, observable } from 'mobx';\nimport { observer } from 'mobx-react-lite';\n\nclass CounterStore {\n  @observable accessor count = 0;\n\n  @computed get doubled(): number {\n    return this.count * 2;\n  }\n\n  @action increment(): void {\n    this.count += 1;\n  }\n}\n\nconst counter = new CounterStore();\n\nexport const Counter = observer(() =\u003e {\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003eCount: {counter.count}\u003c/p\u003e\n      \u003cp\u003eDoubled: {counter.doubled}\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e counter.increment()}\u003e\n        Increment\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n});\n```\n\nThis works, but it leaves open questions:\n\n- When should the store set up subscriptions?\n- When should it clean them up?\n- How do we structure nested state objects?\n- How do we use React hooks in class state?\n- How do we avoid prop-drilling shared state?\n\nThat is where `react-state-object` helps.\n\n## Mental Model: MobX + react-state-object\n\nThink of a `ReactStateObject` as:\n\n- a **MobX-powered class state model**\n- whose lifecycle is controlled by a React component\n- and which may contain child state objects that mount /\n  unmount explicitly when marked\n\nTypical flow:\n\n1. React component creates the class via\n   `useMountStateObject(MyState)`\n2. React owns the instance lifetime\n3. The state object receives `mount()` on component mount\n4. The state object receives `unmount()` on component\n   unmount\n5. Marked child `ReactStateObject`s are mounted/unmounted\n   recursively\n\nThis pattern is common in production apps for app\nlayout state, environment observers (window size /\nelement size), page-level state, and feature-specific\nstate trees.\n\n## Quick Start (Recommended First Example)\n\nThis example shows the full intended pattern with MobX.\n\n```tsx\nimport {\n  action,\n  autorun,\n  computed,\n  observable,\n} from 'mobx';\nimport { observer } from 'mobx-react-lite';\nimport {\n  ReactStateObject,\n  useMountStateObject,\n} from 'react-state-object';\n\nclass CounterState extends ReactStateObject {\n  @observable accessor count = 0;\n\n  @computed get doubled(): number {\n    return this.count * 2;\n  }\n\n  protected mount(): void {\n    // Runs once when the React component mounts.\n    this.withCleanup(() =\u003e {\n      // `autorun` returns a disposer. `withCleanup` stores it\n      // and calls it automatically on unmount.\n      return autorun(() =\u003e {\n        console.log('count is', this.count);\n      });\n    });\n  }\n\n  @action increment(): void {\n    this.count += 1;\n  }\n\n  @action decrement(): void {\n    this.count -= 1;\n  }\n}\n\nexport const CounterScreen = observer((): JSX.Element =\u003e {\n  const state = useMountStateObject(CounterState);\n\n  return (\n    \u003cdiv\u003e\n      \u003cp\u003eCount: {state.count}\u003c/p\u003e\n      \u003cp\u003eDoubled: {state.doubled}\u003c/p\u003e\n      \u003cbutton onClick={() =\u003e state.decrement()}\u003e-\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e state.increment()}\u003e+\u003c/button\u003e\n    \u003c/div\u003e\n  );\n});\n```\n\n## Core Concepts\n\n## `ReactStateObject`\n\nBase class for state objects that need lifecycle and\nchild-state propagation.\n\n```ts\nclass MyState extends ReactStateObject {\n  protected mount(): void {\n    // setup subscriptions, observers, timers, etc.\n  }\n\n  protected unmount(): void {\n    // optional manual cleanup if needed\n  }\n}\n```\n\n### What it adds on top of a plain MobX class\n\n- `mount()` and `unmount()` hooks\n- explicit child state object registration and recursive\n  mount/unmount\n- `withCleanup(...)` helper to register disposer\n  functions\n- `hookIntoLifecycle(...)` for advanced lifecycle\n  registration\n\n### Explicit child lifecycle propagation\n\nIn larger apps, state objects often contain smaller\nones (for example, a layout state containing an\nelement-size observer, a sidebar state object, and\nother focused child state objects).\n\nMark child state objects with `@mountStateObject` when\nthe parent owns their lifecycle. Unmarked references are\nignored by lifecycle traversal.\n\n```ts\nclass FiltersState extends ReactStateObject {\n  @observable accessor query = '';\n}\n\nclass TableState extends ReactStateObject {\n  @mountStateObject\n  @observable accessor filters = new FiltersState();\n}\n\n// Mounting `TableState` also mounts `filters`.\n```\n\nThis makes it easy to create a tree of state objects\nwithout manually coordinating lifecycle for every child.\n\n## `useMountStateObject(StateObjectClass, ...constructorArgs)`\n\nThis is the React hook that creates and owns a\n`ReactStateObject` instance.\n\n```ts\nconst state = useMountStateObject(MyState);\n```\n\n```ts\nconst state = useMountStateObject(\n  UserState,\n  userId\n);\n```\n\n```ts\nconst state = useMountStateObject(\n  UserState,\n  () =\u003e new UserState(userId),\n  [userId]\n);\n```\n\n### What it does\n\n- creates the instance once per component instance while\n  the class identity and tracked dependencies stay the\n  same\n- always treats `StateObjectClass` as a dependency, so a\n  changed class identity recreates the instance\n- treats constructor arguments as dependencies in the\n  `useMountStateObject(StateObjectClass, ...args)` form\n- records any React hooks used by `@fromHook(...)`\n  decorators during initialization\n- replays those hooks on subsequent renders to preserve\n  hook order\n- calls the state object lifecycle (`mount` / `unmount`)\n- recreates the state object when `dependencies` change\n  and reruns its lifecycle\n\n### Important rule (always)\n\nA `ReactStateObject` should **never** be initialized\noutside of `useMountStateObject(...)` when used from\nReact.\n\nAlways create it with:\n\n```ts\nconst state = useMountStateObject(MyState);\n```\n\nDo not create a `ReactStateObject` with plain `new`\ninside a React component (including `useMemo`,\n`useRef`, module-level singletons used as UI state, or\nconditional branches).\n\n## Lifecycle Helpers\n\n## `withCleanup(...)` (most common)\n\nThis is the main lifecycle helper you will use.\n\nIt runs setup logic now and stores the returned cleanup\nfunction for unmount.\n\n```ts\nprotected mount(): void {\n  this.withCleanup(() =\u003e {\n    const id = window.setInterval(() =\u003e {\n      console.log('tick');\n    }, 1000);\n\n    return () =\u003e {\n      window.clearInterval(id);\n    };\n  });\n}\n```\n\nThis pattern is commonly used for:\n\n- `autorun(...)` disposers\n- DOM event listeners (`resize`, etc.)\n- subscriptions / observables\n\n## `hookIntoLifecycle(...)` (advanced)\n\nThis is a lower-level API that lets you register mount /\nunmount callbacks directly.\n\n```ts\nconstructor() {\n  super();\n\n  this.hookIntoLifecycle({\n    onMount: () =\u003e {\n      console.log('mounted');\n    },\n    onUnmount: () =\u003e {\n      console.log('unmounted');\n    },\n  });\n}\n```\n\nIn most apps, `withCleanup(...)` is the primary\npattern; `hookIntoLifecycle(...)` exists for more\ncustom composition scenarios.\n\n## MobX + React Rendering (How UI Updates)\n\n`react-state-object` does **not** automatically make your\ncomponent reactive. React components still need to be\nwrapped with MobX's `observer(...)` (or use another MobX\nReact integration pattern).\n\n```tsx\nimport { observer } from 'mobx-react-lite';\n\nconst UserPanel = observer(() =\u003e {\n  const state = useMountStateObject(UserState);\n\n  // Reading observables here makes this component react.\n  return \u003cdiv\u003e{state.userName}\u003c/div\u003e;\n});\n```\n\nIf you read MobX observables in a component that is not\nan `observer`, React usually will not re-render when they\nchange.\n\n## Using React Hooks Inside a Class (`@fromHook(...)`)\n\nA major feature of this library is the ability to bind a\nclass accessor to a React hook result.\n\nThis is useful when your state object needs data from a\nReact hook such as:\n\n- routing hooks (`useParams`, `useLocation`, `usePage`)\n- context hooks\n- feature hooks that return callbacks/services\n\n### Basic example\n\n```tsx\nimport { observable } from 'mobx';\nimport { useLocation } from 'react-router-dom';\nimport {\n  fromHook,\n  ReactStateObject,\n} from 'react-state-object';\n\nclass RouteState extends ReactStateObject {\n  @fromHook(() =\u003e useLocation())\n  @observable\n  accessor location!: ReturnType\u003ctypeof useLocation\u003e;\n}\n```\n\n### Example with `this` access (common app pattern)\n\n`@fromHook(...)` can receive a function that uses the\nstate object instance as `this`.\n\nThis is useful when the hook needs the state object.\n\n```tsx\nclass ModalState extends ReactStateObject {\n  @fromHook(function (this: ModalState) {\n    return useModalLauncher(this);\n  })\n  @observable\n  accessor openModal!: () =\u003e void;\n}\n```\n\nThis pattern is useful when a feature state object binds\na hook-derived callback or service that needs access to\nthe state instance.\n\n### Rules for `@fromHook(...)`\n\n- Use it on `accessor` properties.\n- Create the state object with `useMountStateObject(...)`.\n- Keep hook usage stable (same hooks in same order across\n  renders), just like normal React rules.\n- Typically combine it with `@observable` when you want\n  MobX reactivity on the accessor value.\n\n### Why injection and hook-backed values are not children by default\n\n`@fromHook(...)` and `@injectInstance(...)` do not\nparticipate in child lifecycle traversal unless they are\nalso decorated with `@mountStateObject`.\n\nThis keeps hook-managed and injected values under their\nown ownership model unless you explicitly declare parent\nownership.\n\n## Class Instance Injection (DI) for React Trees\n\nThis library includes a simple class-instance injection\nsystem. It lets you bind an instance in React and then\nretrieve it later by class.\n\nThis is a strong fit for sharing app-level and page-level\nstate (for example layout state, route/page observers,\nwindow-size observers, and feature state) without prop\ndrilling.\n\n## `InstanceInjectionRoot`\n\nThis provider stores the internal registry of class -\u003e\nReact context mappings.\n\nPut it near the top of your app (once).\n\n```tsx\n\u003cInstanceInjectionRoot\u003e\n  \u003cApp /\u003e\n\u003c/InstanceInjectionRoot\u003e\n```\n\n## `BindInstanceForInjection`\n\nBinds a specific class instance for descendants.\n\n```tsx\n\u003cBindInstanceForInjection instance={appState}\u003e\n  \u003cChildren /\u003e\n\u003c/BindInstanceForInjection\u003e\n```\n\nYou can nest these to provide multiple state objects.\n\n```tsx\n\u003cInstanceInjectionRoot\u003e\n  \u003cBindInstanceForInjection instance={appState}\u003e\n    \u003cBindInstanceForInjection instance={sessionState}\u003e\n      \u003cMyScreen /\u003e\n    \u003c/BindInstanceForInjection\u003e\n  \u003c/BindInstanceForInjection\u003e\n\u003c/InstanceInjectionRoot\u003e\n```\n\n## `useInjectInstance(MyClass)`\n\nReads the nearest instance bound for a class.\nThrows if missing.\n\n```tsx\nimport { observer } from 'mobx-react-lite';\nimport { useInjectInstance } from 'react-state-object';\n\nconst Header = observer(() =\u003e {\n  const appState = useInjectInstance(AppState);\n\n  return \u003ch1\u003e{appState.title}\u003c/h1\u003e;\n});\n```\n\n## `useInjectInstanceOrNull(MyClass)`\n\nSame as `useInjectInstance(...)`, but returns `null`\ninstead of throwing if no bound instance exists on the\ncurrent branch.\n\n```tsx\nconst maybeAppState = useInjectInstanceOrNull(AppState);\n```\n\n## `@injectInstance(...)` (Decorator)\n\n`@injectInstance(...)` is a convenience decorator built on\n`@fromHook(...)`. It injects an instance into a class\naccessor.\n\nThis lets one `ReactStateObject` depend on another\nwithout manually calling hooks in component code.\n\n```tsx\nimport { observable } from 'mobx';\nimport {\n  injectInstance,\n  ReactStateObject,\n} from 'react-state-object';\n\nclass AppState extends ReactStateObject {\n  @observable accessor title = 'Dashboard';\n}\n\nclass PageState extends ReactStateObject {\n  @injectInstance(AppState)\n  @observable\n  accessor appState!: AppState;\n}\n```\n\nThis is a common pattern in larger apps, where feature\nstate objects inject app-level or page observer state\nobjects.\n\n## End-to-End Example: App State + Page State + UI\n\nThis example combines MobX, `useMountStateObject`,\ninjection, and `observer(...)`.\n\n```tsx\nimport { action, computed, observable } from 'mobx';\nimport { observer } from 'mobx-react-lite';\nimport {\n  BindInstanceForInjection,\n  injectInstance,\n  InstanceInjectionRoot,\n  ReactStateObject,\n  useInjectInstance,\n  useMountStateObject,\n} from 'react-state-object';\n\nclass AppState extends ReactStateObject {\n  @observable accessor appName = 'School Portal';\n}\n\nclass CounterPageState extends ReactStateObject {\n  @injectInstance(AppState)\n  @observable\n  accessor appState!: AppState;\n\n  @observable accessor count = 0;\n\n  @computed get title(): string {\n    return `${this.appState.appName} (${this.count})`;\n  }\n\n  @action increment(): void {\n    this.count += 1;\n  }\n}\n\nconst CounterPageBody = observer(() =\u003e {\n  const pageState = useInjectInstance(CounterPageState);\n\n  return (\n    \u003cdiv\u003e\n      \u003ch2\u003e{pageState.title}\u003c/h2\u003e\n      \u003cbutton onClick={() =\u003e pageState.increment()}\u003e\n        Increment\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n});\n\nconst CounterPage = observer(() =\u003e {\n  const pageState =\n    useMountStateObject(CounterPageState);\n\n  return (\n    \u003cBindInstanceForInjection instance={pageState}\u003e\n      \u003cCounterPageBody /\u003e\n    \u003c/BindInstanceForInjection\u003e\n  );\n});\n\nexport const App = () =\u003e {\n  const appState = useMountStateObject(AppState);\n\n  return (\n    \u003cInstanceInjectionRoot\u003e\n      \u003cBindInstanceForInjection instance={appState}\u003e\n        \u003cCounterPage /\u003e\n      \u003c/BindInstanceForInjection\u003e\n    \u003c/InstanceInjectionRoot\u003e\n  );\n};\n```\n\n## Example: DOM Observer State (ResizeObserver pattern)\n\nA common pattern is using a state object to wrap browser\nAPIs like `ResizeObserver`.\n\n```tsx\nimport { action, observable } from 'mobx';\nimport {\n  ReactStateObject,\n  useMountStateObject,\n} from 'react-state-object';\nimport { observer } from 'mobx-react-lite';\n\nclass ElementSizeState extends ReactStateObject {\n  @observable accessor width = 0;\n  @observable accessor height = 0;\n\n  private readonly resizeObserver = new ResizeObserver(\n    ([entry]) =\u003e {\n      if (!entry) return;\n      this.setSize(\n        entry.contentRect.width,\n        entry.contentRect.height\n      );\n    }\n  );\n\n  readonly elementRef = (\n    el: HTMLDivElement | null\n  ): void =\u003e {\n    this.resizeObserver.disconnect();\n    if (el) this.resizeObserver.observe(el);\n  };\n\n  protected unmount(): void {\n    this.resizeObserver.disconnect();\n  }\n\n  @action private setSize(\n    width: number,\n    height: number\n  ): void {\n    this.width = width;\n    this.height = height;\n  }\n}\n\nexport const MeasuredPanel = observer(() =\u003e {\n  const size = useMountStateObject(ElementSizeState);\n\n  return (\n    \u003cdiv\u003e\n      \u003cdiv ref={size.elementRef}\u003eResize me\u003c/div\u003e\n      \u003cp\u003e\n        {size.width} x {size.height}\n      \u003c/p\u003e\n    \u003c/div\u003e\n  );\n});\n```\n\nThis pattern keeps browser API setup/cleanup inside a\nstate class instead of scattering it across components.\n\n## Example: Using a Hook-Derived Service in State\n\nThis mirrors a common production pattern where a state\nobject gets a hook-derived function (for example a toast\nhelper) and calls it inside an action.\n\n```tsx\nimport { action, observable } from 'mobx';\nimport {\n  fromHook,\n  ReactStateObject,\n} from 'react-state-object';\n\nfunction useNotifier(): {\n  success: (msg: string) =\u003e void;\n} {\n  return {\n    success: (msg) =\u003e console.log('SUCCESS:', msg),\n  };\n}\n\nclass SaveState extends ReactStateObject {\n  @observable accessor isSaving = false;\n\n  @fromHook(() =\u003e useNotifier())\n  @observable\n  accessor notifier!: ReturnType\u003ctypeof useNotifier\u003e;\n\n  @action async save(): Promise\u003cvoid\u003e {\n    this.isSaving = true;\n\n    try {\n      await new Promise((resolve) =\u003e\n        setTimeout(resolve, 300)\n      );\n      this.notifier.success('Saved successfully');\n    } finally {\n      this.isSaving = false;\n    }\n  }\n}\n```\n\n## How This Is Commonly Used in Larger Apps (Pattern Summary)\n\nBased on common production usage patterns, a common\nstructure is:\n\n1. **App-level state objects**\n   - created near app root with `useMountStateObject(...)`\n   - bound with `BindInstanceForInjection`\n   - examples: theme state, page observer state, window\n     size observer\n\n2. **Page-level state objects**\n   - created in page/layout components\n   - may inject app-level state via `@injectInstance(...)`\n   - may compose nested child state objects with\n     `@mountStateObject`\n\n3. **Feature sub-state objects**\n   - nested inside page state (forms, sidebar state,\n     tables, save state, UI state)\n   - mounted/unmounted explicitly through parent\n     `ReactStateObject`\n\n4. **React components**\n   - wrapped in `observer(...)`\n   - either read state directly via props or retrieve it\n     via `useInjectInstance(...)`\n\nThis architecture keeps React components focused on UI\nwhile moving orchestration/subscriptions into state\nclasses.\n\nImportant: every `ReactStateObject` in this structure\nshould still be created through `useMountStateObject(...)`\nat the React boundary that owns it. Child state objects\ncan be instantiated inside parent state object\nconstructors because their lifecycle is then managed by\nthe parent `ReactStateObject` created via\n`useMountStateObject(...)`.\n\n## API Reference\n\n## `ReactStateObject`\n\nBase class with lifecycle and child-state propagation.\n\nMethods you typically override:\n\n- `protected mount(): void`\n- `protected unmount(): void`\n\nHelpers:\n\n- `protected withCleanup(action: () =\u003e () =\u003e void): void`\n- `protected hookIntoLifecycle({ onMount?, onUnmount? })`\n\n## `useMountStateObject(StateObjectClass, ...constructorArgs)`\n\nCreates a `ReactStateObject` and ties it to React\ncomponent lifecycle. The class identity is always a\ndependency. In the constructor-argument form, those\narguments are dependencies automatically. In the custom\nfactory form, the explicit dependency array is used in\naddition to the class identity.\n\n```ts\nconst state = useMountStateObject(MyState);\n```\n\n```ts\nconst state = useMountStateObject(\n  UserState,\n  userId\n);\n```\n\n```ts\nconst state = useMountStateObject(\n  UserState,\n  () =\u003e new UserState(userId),\n  [userId]\n);\n```\n\nThis is the required creation path for `ReactStateObject`\ninstances used by React components.\n\n## `fromHook(hookFn)`\n\nAccessor decorator that assigns a React hook result to a\nstate object accessor.\n\n```ts\n@fromHook(() =\u003e useSomeHook())\n@observable\naccessor value!: ReturnType\u003ctypeof useSomeHook\u003e;\n```\n\n## `injectInstance(Class)`\n\nAccessor decorator that injects a class instance from the\ninjection tree using `useInjectInstance(...)`.\n\n```ts\n@injectInstance(AppState)\n@observable\naccessor appState!: AppState;\n```\n\n## `invokeReactStateObjectHook(hook)`\n\nLow-level hook registration helper used internally by\n`fromHook(...)`. Most users should not call this\nmanually.\n\n## `InstanceInjectionRoot`\n\nRoot provider for the class-instance injection registry.\n\n## `BindInstanceForInjection`\n\nBinds an instance for descendants.\n\n## `useInjectInstance(Class)`\n\nGets the nearest instance for a class or throws.\n\n## `useInjectInstanceOrNull(Class)`\n\nGets the nearest instance for a class or returns `null`.\n\n## Common Mistakes (and Fixes)\n\n## 1) Component does not re-render when state changes\n\nCause:\n\n- the component is not wrapped in `observer(...)`\n\nFix:\n\n- wrap the component with `observer` from\n  `mobx-react-lite`\n\n## 2) `fromHook(...)` throws an error about\n\n`useMountStateObject`\n\nCause:\n\n- you created a `ReactStateObject` with `new MyState()`\n  directly in a component instead of using\n  `useMountStateObject(...)`\n\nFix:\n\n- create it with `useMountStateObject(...)`\n\n## 3) `useInjectInstance(...)` throws that no instance was\n\nbound\n\nCause:\n\n- missing `InstanceInjectionRoot`\n- missing `BindInstanceForInjection`\n- bound instance is on a different branch of the tree\n\nFix:\n\n- add `InstanceInjectionRoot` near the app root\n- ensure the consuming component is a descendant of the\n  matching `BindInstanceForInjection`\n\n## 4) Hook order errors in `fromHook(...)`\n\nCause:\n\n- conditional hook usage inside `@fromHook(...)`\n\nFix:\n\n- keep hook usage unconditional and stable, same as normal\n  React hook rules\n\n## Publishing / Development\n\n### Scripts\n\n- `npm run format`\n- `npm run lint`\n- `npm run typecheck`\n- `npm run build`\n\n### Prettier config\n\nThis package copies the author's existing Prettier setup\nstyle,\nincluding:\n\n- `.prettierrc`\n- `.prettierignore`\n- `prettier-plugin-organize-imports`\n- matching `format` and `lint-staged` scripts\n\n## License\n\n`UNLICENSED` (update before publishing publicly if\nneeded).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatrickjames242%2Freact-state-object","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpatrickjames242%2Freact-state-object","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatrickjames242%2Freact-state-object/lists"}