{"id":15414893,"url":"https://github.com/lxsmnsyc/react-external-subject","last_synced_at":"2026-02-27T13:02:55.886Z","repository":{"id":71067497,"uuid":"319648940","full_name":"lxsmnsyc/react-external-subject","owner":"lxsmnsyc","description":"Wrap mutable sources into React-safe mutable sources","archived":false,"fork":false,"pushed_at":"2020-12-26T04:54:18.000Z","size":637,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-09T14:26:56.893Z","etag":null,"topics":["concurrent-mode","mutable-state","react","subscription"],"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/lxsmnsyc.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}},"created_at":"2020-12-08T13:35:55.000Z","updated_at":"2020-12-26T04:54:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"0908516d-c621-4a50-87ed-8d1643107e84","html_url":"https://github.com/lxsmnsyc/react-external-subject","commit_stats":{"total_commits":49,"total_committers":1,"mean_commits":49.0,"dds":0.0,"last_synced_commit":"a5fe6c545a7945ec59bf1aa0b44d508c5019b062"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lxsmnsyc/react-external-subject","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lxsmnsyc%2Freact-external-subject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lxsmnsyc%2Freact-external-subject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lxsmnsyc%2Freact-external-subject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lxsmnsyc%2Freact-external-subject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lxsmnsyc","download_url":"https://codeload.github.com/lxsmnsyc/react-external-subject/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lxsmnsyc%2Freact-external-subject/sbom","scorecard":{"id":606885,"data":{"date":"2025-08-11","repo":{"name":"github.com/lxsmnsyc/react-external-subject","commit":"a5fe6c545a7945ec59bf1aa0b44d508c5019b062"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.2,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/main.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/lxsmnsyc/react-external-subject/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/lxsmnsyc/react-external-subject/main.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/lxsmnsyc/react-external-subject/main.yml/main?enable=pin","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":0,"reason":"64 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-qxrj-hx23-xp82","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-74fj-2j2h-c42q","Warn: Project is vulnerable to: GHSA-pw2r-vq6v-hr8c","Warn: Project is vulnerable to: GHSA-jchw-25xp-jwwc","Warn: Project is vulnerable to: GHSA-cxjh-pqwp-8mfp","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-593f-38f6-jp5m","Warn: Project is vulnerable to: GHSA-x2rg-q646-7m2v","Warn: Project is vulnerable to: GHSA-jgmv-j7ww-jx2x","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-5rrq-pxf6-6jx5","Warn: Project is vulnerable to: GHSA-8fr3-hfg3-gpgp","Warn: Project is vulnerable to: GHSA-gf8q-jrpm-jvxq","Warn: Project is vulnerable to: GHSA-2r2c-g63r-vccr","Warn: Project is vulnerable to: GHSA-cfm4-qjh2-4765","Warn: Project is vulnerable to: GHSA-x4jg-mjrx-434g","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-566m-qj78-rww5","Warn: Project is vulnerable to: GHSA-hwj9-h5mp-3pm3","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc","Warn: Project is vulnerable to: GHSA-mv48-hcvh-8jj8","Warn: Project is vulnerable to: GHSA-353f-5xf4-qw67","Warn: Project is vulnerable to: GHSA-64vr-g452-qvp3","Warn: Project is vulnerable to: GHSA-9cwx-2883-4wfx","Warn: Project is vulnerable to: GHSA-vg6x-rcgg-rjx6","Warn: Project is vulnerable to: GHSA-x574-m823-4x7w","Warn: Project is vulnerable to: GHSA-4r4m-qw57-chr8","Warn: Project is vulnerable to: GHSA-xcj6-pq6g-qj4x","Warn: Project is vulnerable to: GHSA-356w-63v5-8wf4","Warn: Project is vulnerable to: GHSA-859w-5945-r5v3","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-rxrc-rgv4-jpvx","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T01:48:27.057Z","repository_id":71067497,"created_at":"2025-08-21T01:48:27.057Z","updated_at":"2025-08-21T01:48:27.057Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279249046,"owners_count":26133926,"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","status":"online","status_checked_at":"2025-10-16T02:00:06.019Z","response_time":53,"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":["concurrent-mode","mutable-state","react","subscription"],"created_at":"2024-10-01T17:05:12.322Z","updated_at":"2025-10-16T22:30:42.430Z","avatar_url":"https://github.com/lxsmnsyc.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-external-subject\n\n\u003e Turn third-party mutable sources into React-safe source\n\n[![NPM](https://img.shields.io/npm/v/react-external-subject.svg)](https://www.npmjs.com/package/react-external-subject) [![JavaScript Style Guide](https://badgen.net/badge/code%20style/airbnb/ff5a5f?icon=airbnb)](https://github.com/airbnb/javascript)[![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?style=flat-square\u0026logo=codesandbox)](https://codesandbox.io/s/github/LXSMNSYC/react-external-subject/tree/main/example/)\n\n## Install\n\n```bash\nyarn add react-external-subject\n```\n\n## Usage\n\n```tsx\nimport {\n  ExternalSubjectSynchronizer,\n  createExternalSubject,\n  useExternalSubject,\n  } from 'react-external-subject';\n\n// An example of a mutable source\nlet source = 0;\n\n// Wrap our mutable source into an external subject\nconst subject = createExternalSubject({\n  read: () =\u003e source,\n});\n\n// Create a component that reads from our mutable source\nfunction ObserverA() {\n  const value = useExternalSubject(subject, false);\n  return \u003ch1\u003eValue: {value}\u003c/h1\u003e;\n}\n\n// Create a component similar to ObserverA but suspends\nfunction ObserverB() {\n  const value = useExternalSubject(subject, true);\n  return \u003ch1\u003eValue: {value}\u003c/h1\u003e;\n}\n\n// Create a component with a render-time side-effect\nfunction Effectful() {\n  // Update our source\n  source = 1337;\n  return \u003ch1\u003eEffectful: {source}\u003c/h1\u003e;\n}\n\nfunction App() {\n  return (\n    \u003cExternalSubjectSynchronizer\u003e\n      \u003cObserverA /\u003e\n      \u003cEffectful /\u003e\n      {/*\n        By the time ObserverB renders, the source has already \n        updated, creating a \"tear\".\n      */}\n      \u003cObserverB /\u003e\n    \u003c/ExternalSubjectSynchronizer\u003e\n  );\n}\n```\n\n## Features\n\n`react-external-subject` is a library that wraps mutable sources not usually managed by React into a React-safe external subject.\n\nWhen a component subscribes to an external subject, the external subject maintains and keeps track of a state that is safe for all components to share and render.\n\nIf it detects a tear in between renders, or between render and commit phase, the external subject asynchronously requests an update to the safe state and notifies all components to update their received value.\n\nComponents with suspense mode active will suspend until they receive the new safe state.\n\n### Memoization\n\nExternal subjects may receive a memoization option through `options.shouldUpdate`. This function is used internally to compare the proposed state and the safe state. By default, it uses the `Object.is` function.\n\n### Subscription\n\nExternal subjects may subscribe to a mutable source through `options.subscribe`. This function receives a callback that is used to subscribe to the source for receiving updates. `options.subscribe` may return a cleanup function, potentially for cleaning subscription.\n\n### Lazy Subscription\n\nExternal subjects may lazily subscribe/unsubscribe through `options.lazySubscribe`. When set to true, subjects only begins subscribing to the mutable source when a listener is attached to them (e.g. through `useExternalSubject`.). If the subject has zero listeners, the subject automatically unsubscribes. By default, external subjects subscribes immediately on creation and never unsubscribes.\n\n### Hook\n\n`useExternalSubject(subject, suspense = false)` is used to observe external subjects' state. If `suspense` is set to false, `useExternalSubject` will return the last safe state if it detects a tearing during render, otherwise, `useExternalSubject` suspends the component until the new safe state is updated. Regardless, `useExternalSubject` will always guarantee that the component will receive a state that is safe to render as well as all of the components will receive the same value, preventing the UI from tearing.\n\n### Alternatives\n\n- [`use-subscription`](https://www.npmjs.com/package/use-subscription) provides a mechanism that allows the component to update immediately when it detects that the value has gone stale between render and commit phase. However, this does not prevent the component from tearing during render phase alone.\n- [`useMutableSource`](https://github.com/reactjs/rfcs/blob/master/text/0147-use-mutable-source.md) is an upcoming first-party React hook that wraps mutable sources into React-safe (Concurrent-safe, that is) mutable sources. The hook is also partially the basis of this whole library, the difference is that `useMutableSource` keeps tracks of the mutable source's version in contrast with `useExternalSubject` that keeps tracks the state of the mutable source. `useMutableSource` also works with the internal Fiber architecture to handle tearing.\n\n## License\n\nMIT © [lxsmnsyc](https://github.com/lxsmnsyc)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flxsmnsyc%2Freact-external-subject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flxsmnsyc%2Freact-external-subject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flxsmnsyc%2Freact-external-subject/lists"}