{"id":16695215,"url":"https://github.com/orir/react-composition-provider","last_synced_at":"2025-04-10T01:42:59.081Z","repository":{"id":143905041,"uuid":"164580627","full_name":"OriR/react-composition-provider","owner":"OriR","description":"HOC for creating components using the Composition Provider pattern","archived":false,"fork":false,"pushed_at":"2019-01-12T19:34:18.000Z","size":370,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-04-25T09:02:14.259Z","etag":null,"topics":["component","react","react-context"],"latest_commit_sha":null,"homepage":"","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/OriR.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-08T06:35:24.000Z","updated_at":"2022-07-11T18:53:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"34969a33-38a6-4f53-8f6c-12236457d5c8","html_url":"https://github.com/OriR/react-composition-provider","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/OriR%2Freact-composition-provider","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OriR%2Freact-composition-provider/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OriR%2Freact-composition-provider/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OriR%2Freact-composition-provider/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OriR","download_url":"https://codeload.github.com/OriR/react-composition-provider/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248142298,"owners_count":21054616,"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":["component","react","react-context"],"created_at":"2024-10-12T17:05:58.779Z","updated_at":"2025-04-10T01:42:59.055Z","avatar_url":"https://github.com/OriR.png","language":"JavaScript","readme":"# react-composition-provider\n\n`react-composition-provider` is a small react library that helps you implement a Composition Provider pattern.\n\nThe Composition Provider pattern tries to make prop drilling more ergonomic.\n\n## Install\n\n```\nnpm i --save react-composition-provider\n```\n\n## Usage\n\nLet's look at a simple example of `Menu`, `Card`, `CardList`, `NewsList` \u0026 `WeatherList` components. \u003c/br\u003e\nWhere `CardList` contains `Card` and `Card` contains `Menu`. \u003c/br\u003e\nWhere `NewsList` \u0026 `WeatherList` both contain `CardList`. \u003c/br\u003e\n\nWe want to pass props from `NewsList`  \u0026 `WeatherList` to `Menu`, this is how you do it:\n\n```js\n// Menu.js\nimport { withCompositionProvider } from 'react-composition-provider';\n\nconst InnerMenu = ({ cardId, items }) =\u003e {\n  // This is where the actual menu code would be.\n};\n\nexport default withCompositionProvider(InnerMenu);\n\n// Card.js\nimport Menu from './Menu';\n\nexport default (props) =\u003e {\n    return (\n        \u003cdiv\u003e\n            \u003cMenu cardId={props.id}/\u003e\n        \u003c/dib\u003e\n    )\n}\n```\n\nAnd in `NewsList` \u0026 `WeatherList` you'd do this:\n\n```js\n// NewsList.js\nimport Menu from 'Menu';\nexport default (props) =\u003e {\n    const actions = [{ text: 'remove' }, { text: 'star' }];\n    return (\n        \u003cMenu.Provider items={actions} \u003e\n            \u003cCardList cards={props.news}/\u003e\n        \u003c/Menu.Provider\u003e\n    )\n}\n\n// WeatherList.js\nimport Menu from 'Menu';\nexport default (props) =\u003e {\n    const actions = [{ text: 'remove location' }];\n    return (\n        \u003cMenu.Provider items={actions} \u003e\n            \u003cCardList cards={props.locations}/\u003e\n        \u003c/Menu.Provider\u003e\n    )\n}\n```\n\nAnd that's it!\u003c/br\u003e\nNow every time you want to supply extra props to a decendant `Menu` component just render `Menu.Provider` with what you need!\n\nThis is the basic usage but the library gives you a lot more features than just this, like:\n\n1. You can have multiple `Menu.Provider` in the same tree, that way even if someone is using `Menu.Provider` in a component you can't change you'll still be able to pass more props to it!\n2. If you want to create a scope and make sure that no ancestor of type `Menu.Provider` can add props you can use `Menu.Provider.Scoped`.\n3. It handles refs correctly, you wouldn't want to write `\u003cMenu.Provider ref={...}` and get the ref of the provider, right?\n4. It can handle composition elegantly (already does so OOTB for, `ref`, `style`, `className` and `children`) and gives you a way to define the composition for each prop.\n5. When you want to re-define the children of the inner component you'd have to use a prop named `kids` because obviously the `children` prop is already taken.\n6. Allows you to conditionally mount the component. Even if the `Menu` was rendered somewhere, it doesn't mean that `InnerMenu` has to be mounted.\n7. Gives you a way to control which props can be propagated from a `Menu.Provider` and which aren't.\n\nMost of these can be configured through a second options argument to `withCompositionProvider`:\n\n```js\nimport { withCompositionProvider, composers } from 'react-composition-provider';\n\nexport default withCompositionProvider(MyComponent, {\n  // Object defining all the polyfills used by this package.\n  polyfills: {\n    // If you're using an old version of react that doesn't have React.createContext,\n    // you'll need to supply a polyfill for it, something like - https://github.com/jamiebuilds/create-react-context\n    createContext: React.createContext\n  }\n  // Allows you to control when the inner component actually mounts.\n  // Returns true if the component should mount and false when it shouldn't.\n  // props - all the composed props for this component\n  // propsChain - an array of props, ordered by closeness to the actual component.\n  //              where propsChain[0] is given directly to the component and propsChain[1] is the closest Provider, and so on.\n  mountWhen: ({ props, propsChain }) =\u003e true,\n  // An array of props to ignore from all the Providers up the chain.\n  ignore: [],\n  // The most advanced configuration.\n  // Gives you full control over the composition of each and every prop.\n  // Each key in this object is the prop name and the value is a function that composes it and returns said composition.\n  // Luckily, you don't have to think about it too much because react-composition-provider comes with a built-in set of composers.\n  // To use these composers just import it like this:\n  // import { composers } from 'react-composition-providers';\n  //\n  // There are 2 available variations for some of the composers, reverse or take.\n  // reverse - composes the same way but just in reverse order.\n  // take - an object with first, last \u0026 index that takes a single element from the array.\n  // Note that all of the composers implement the take capabiliy, the ones marked with reverse can also use that capability.\n  //\n  // These are the available composers:\n  //  1. composers.node()                                            - combines an array of nodes as siblings using React.Fragment. (reverse)\n  //  2. composers.string(separator: String)                         - combines an array of strings with the given separator (defaults to \" \" - space). (reverse)\n  //  3. composers.object(merge: (objects: Array\u003cObject\u003e) =\u003e Object) - combines an array of objects by merging them together.\n  //                                                                   the first one is the least specific and the last is the most specific.\n  //                                                                   the merge callback takes in an array of objects and returns a merged object (defaults to Object.assign). (reverse)\n  //  4. composers.array()                                           - combines an array of arrays to a single array (essentially a flatMap). (reverse)\n  //  5. composers.func.result.compose(composer: Composer)           - executes each function in the array sequentially, the composer parameter is used to combine the results.\n  //  6. composers.func.result.ignore()                              - executes each function in the array sequentially and ignores the result.\n  //  7. composers.bool.and()                                        - combines an array of booleans to a single boolean value by \"\u0026\u0026\" all of them together.\n  //  8. composers.bool.or()                                         - combines an array of booleans to a single boolean value by \"||\" all of them together.\n  //  9. composers.number.sum()                                      - gets the sum of the array of numbers.\n  //  10. composers.number.min()                                     - gets the minimum number of the array of numbers.\n  //  11. composers.number.max()                                     - gets the maximum number of the array of numbers\n  //  12. composers.number.average()                                 - gets the average of the array of numbers.\n  //  13. composers.date.min()                                       - gets the minimum date of the array of dates.\n  //  14. composers.date.max()                                       - gets the maximum date of the array of dates.\n  compose: {},\n});\n```\n\nAs you can see this API is very flexible, but tries to have sane defaults.\n\n## Why?\nProp drilling makes our components too specific, and tie the API of a component with the components it's composing (its implementation). \u003c/br\u003e\nWhen these components change it will probably mean that the API will change too.\nThis applies to all the levels you \"drill\" the props, if something way down the line changes - all of them change. \u003c/br\u003e\nWhat happens when you forget to drill some props? that's a tedious job of going through all the levels between where you have the prop value all the way down to the where it actually needs to be. \u003c/br\u003e\n\nYou might say that you can solve all of that with a state management library like `redux` - that's true. \u003c/br\u003e\nYou'd then need to define reducers, actions, and connect components to a store - just to pass some props down the line. It's like using a hammer for screws. \u003c/br\u003e\n\nThat's why `react-composition-provider` exists.\u003c/br\u003e\nIt creates a \"portal\" between the component itself and all the places you defined props for it.\u003c/br\u003e\nIt's very small (6kb non-gzipped) so you won't feel it, plus it'll probably help you save some boilerplate code so you code end up with a smaller bundle!.\u003c/br\u003e\n\n## Use cases\n\nAlthough this pattern can be very helpful and you're maybe thinking to yourself \"I'm going to define all of my components like this now!\".\n\n**JUST DON'T**\n\nGeneral rules of thumb (there are probably more since this is new) of dos and don'ts with this pattern:\n\n1. you're in control of both ends, the component itself and the provider(s).\n   1. you have to pass several props down the component tree.\n   2. you have a very deep component tree and don't want to cause re-rendering for all the components in the middle.\n2. as a library author, exposing _some_ internal component API - this may make the library API less predictable.\n   1. when you define props like `xxxProps` - you already want to expose the props for `xxx` as part of your API.\n   2. it's important to also define a scope for your components so things don't leak outside your API boundary.\n\nSo, as with all patterns out there - make sure you understand what you're doing and don't abuse it.\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forir%2Freact-composition-provider","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Forir%2Freact-composition-provider","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Forir%2Freact-composition-provider/lists"}