{"id":15729121,"url":"https://github.com/slorber/react-updatable-context","last_synced_at":"2025-03-31T02:29:47.991Z","repository":{"id":57346999,"uuid":"142423084","full_name":"slorber/react-updatable-context","owner":"slorber","description":"React context which inject a value setter/updater in the Consumer","archived":false,"fork":false,"pushed_at":"2018-09-20T08:31:01.000Z","size":29,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-06T07:31:53.698Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/slorber.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-26T10:01:45.000Z","updated_at":"2018-09-20T08:31:02.000Z","dependencies_parsed_at":"2022-08-31T22:00:28.083Z","dependency_job_id":null,"html_url":"https://github.com/slorber/react-updatable-context","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slorber%2Freact-updatable-context","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slorber%2Freact-updatable-context/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slorber%2Freact-updatable-context/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slorber%2Freact-updatable-context/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slorber","download_url":"https://codeload.github.com/slorber/react-updatable-context/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246406279,"owners_count":20771917,"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":[],"created_at":"2024-10-03T23:20:23.183Z","updated_at":"2025-03-31T02:29:47.972Z","avatar_url":"https://github.com/slorber.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"NOTE: I created this tool, but this is basically an inferior version of [Unstated](https://github.com/jamiebuilds/unstated) that you should use instead. If you need an HOC for unstated you can create one easily on top of the render prop api:\n\n```js\nexport const connectContainer = (\n  Container,\n  mapContainerToProps = () =\u003e null,\n) =\u003e WrappedComponent =\u003e {\n  const PureWrappedComponent = pure(WrappedComponent);\n  const ConnectedComponent = ownProps =\u003e (\n    \u003cSubscribe to={[Container]}\u003e\n      {container =\u003e {\n        const mappedProps = mapContainerToProps(container, ownProps);\n        //console.debug('mappedProps', mappedProps, container);\n        return (\n          \u003cPureWrappedComponent\n            {...mappedProps}\n            {...ownProps} // own props can always overrides connected props\n          /\u003e\n        );\n      }}\n    \u003c/Subscribe\u003e\n  );\n\n  ConnectedComponent.displayName = `connectContainer(${getDisplayName(\n    WrappedComponent,\n  )})`;\n\n  return ConnectedComponent;\n};\n```\n\n\n\nReact Updatable Context\n==========================\n\nSometimes it's annoying to have to manage the state outside of the Provider. \n\nSometimes we just want to make the Provider stateful and be able to update it from Consumers.\n\nThe React context API is a low-level API, it permits to pass down a value to a component tree and that's it. \nThis library tries to build new primitives on this API so that it's easier to use context in real life applications.\n\n```\nyarn add react-updatable-context\nnpm install react-updatable-context\n```\n\n# Examples\n\nLet's first define a very simple language selector component, and in the following examples we'll see how to connect this stateless component to our context.\n\n```jsx harmony\nconst LanguageSelector = ({language, updateLanguage}) =\u003e (\n  \u003cdiv\u003e\n    \u003cdiv\u003eCurrent language: {language}\u003c/div\u003e\n    \u003cdiv onClick={() =\u003e updateLanguage(\"french\")}\u003eSwitch to french\u003c/div\u003e\n    \u003cdiv onClick={() =\u003e updateLanguage(\"english\")}\u003eSwitch to english\u003c/div\u003e\n  \u003c/div\u003e\n);\n```\n\n\n\n### Simple example\n\n```jsx harmony\n\nimport { createUpdatableContext } from 'react-updatable-context';\n\nconst { \n  Provider: LanguageProvider, \n  Consumer: LanguageConsumer,\n} = createUpdatableContext(); \n\n// Add the provider to your app like with normal Context api\nconst MyApp = () =\u003e (\n  \u003cLanguageProvider \n    initialValue={\"english\"} \n    beforeChange={(newLanguage,oldLanguage) =\u003e console.log(\"Language will be updated\",newLanguage,oldLanguage)}\n    afterChange={(newLanguage,oldLanguage) =\u003e console.log(\"Language has been updated\",newLanguage,oldLanguage)}\n  \u003e\n    \u003cRouter/\u003e\n  \u003c/LanguageProvider\u003e\n);\n\n// Use the consumer like normal Context api, but it also receives a 2nd parameter\nconst AppLanguageSelector = () =\u003e (\n  \u003cLanguageConsumer\u003e\n    {(value, update) =\u003e (\n      \u003cLanguageSelector language={value} updateLanguage={update} /\u003e\n    )}\n  \u003c/LanguageConsumer\u003e\n)\n``` \n\n\n\n\n### Managing your global app state with Context\n\nSometimes the level of indirection with events/actions, and tools like Redux, is too much. \nUsing a simple global state API on top of Context can be good enough for many apps.\n\nBut... We don't want to expose the state structure to deeply nested components. \n\n`createSubConsumer` and `connect` are here for that.\n\n\n```jsx harmony\n\nimport { createUpdatableContext } from 'react-updatable-context';\n\nconst {\n  Provider, \n  Consumer,\n  createSubConsumer,\n  connect,\n } = createUpdatableContext({\n  defaultValue: null, // You can set default value, like regular React context\n}); \n\n\nconst MyApp = () =\u003e (\n  \u003cProvider \n    initialValue={{\n      language: \"english\",\n      unreadChatCount: 0,\n    }} \n  \u003e\n    \u003cRouter/\u003e\n  \u003c/Provider\u003e\n);\n\n\n// Then you can use the global consumer directly, but your component is aware of the global state structure\nconst AppLanguageSelector = () =\u003e (\n  \u003cConsumer\u003e\n    {(value, update) =\u003e (\n      \u003cLanguageSelector \n        language={value.language} \n        updateLanguage={language =\u003e update({language})} \n      /\u003e\n    )}\n  \u003c/Consumer\u003e\n);\n\n// You can use a \"sub consumer\" which will refine the value/update api so that you can only get/set the language\nconst LanguageConsumer = createSubConsumer(\n  value =\u003e value.language, // mapValue (1st arg), it's a \"selector\" so you can use reselect if you want\n  update =\u003e language =\u003e update({language}), // mapUpdate (2nd arg)\n);\n\n// Then you can write this instead, which expose less internal state structure\nconst AppLanguageSelector = () =\u003e (\n  \u003cLanguageConsumer\u003e\n    {(value, update) =\u003e (\n      \u003cLanguageSelector \n        language={value} \n        updateLanguage={update} \n      /\u003e\n    )}\n  \u003c/LanguageConsumer\u003e\n);\n\n// You can also use `connect` if you prefer HOCs and an API similar to react-redux\nconst AppLanguageSelector = connect(\n  (value,update) =\u003e ({\n    language: value.language,\n    updateLanguage: language =\u003e update({language}),\n  }),\n)(LanguageSelector)\n``` \n\n\n### Performance and pure components\n\nFor performance reasons, we want to inject stable callbacks when connecting to components.\n\nOn every global state changes, all the consumers will be invoked, and we don't want pure components that consume only a small amount of state to render unnecessarily.\n\nFor that, it is possible to create the callbacks only once, at creation time.\n \nSomehow, you are defining an update API that will replace the raw low-level update API that is injected as the 2nd arg of the consumer\n\n```jsx harmony\nconst {\n  // ...\n  connect,\n } = createUpdatableContext({\n  defaultValue: null,\n  \n  // This creates an \"update API\"\n  mapUpdate: (update,getValue) =\u003e ({\n    updateLanguage: language =\u003e update({language}),\n    incrementUnreadChatCount: () =\u003e update({unreadChatCount: getValue().unreadChatCount + 1}),\n  }),\n}); \n\n// Then you can connect with a stable callback:\nconst AppLanguageSelector = connect(\n  (value,updateApi) =\u003e ({\n    language: value.language,\n    updateLanguage: updateApi.updateLanguage,\n  }),\n)(LanguageSelector)\n\n// Or with a sub-consumer\nconst LanguageConsumer = createSubConsumer(\n  value =\u003e value.language,\n  updateApi =\u003e language =\u003e updateApi.updateLanguage(language),\n);\n```\n\n### Using a reducer\n\nTODO reducer/dispatch example for those liking evented systems\n\n### Using async update functions\n\nTODO\n\n### Optimistic updates\n\nTODO\n\n\n\n# FAQ\n\n### Is this another state management library?\n\nNo. You can use this lib for whatever you want (including global state management) as long as it makes your life simple. For example, if you have a complex page and passing props and callbacks down becomes complicated you can create an updatable context fot that page.\n\n### Does it work with React-Native?\n\nYes. Sometimes I want to share state between react-navigation screens (for example a multi-screen wizzard). That can be a replacement for `screenProps` that are not recommended to use anymore.\n\nYou can wrap your navigator with an updatable context provider for that:\n\n```jsx harmony\nconst MyStackNavigator = createStackNavigator(...);\n\nconst MyStatefulStackNavigator = (props) =\u003e (\n  \u003cProvider initialValue={myInitialNavigatorState}\u003e\n    \u003cMyStackNavigator {...props}/\u003e\n  \u003c/Provider\u003e\n);\nMyStatefulStackNavigator.router = MyStackNavigator.router;\n```\n\n# TODO\n\n- Complete examples\n- Support updates with a function (like `setState(oldState =\u003e newState)`)\n\n# License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslorber%2Freact-updatable-context","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslorber%2Freact-updatable-context","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslorber%2Freact-updatable-context/lists"}