{"id":13437721,"url":"https://github.com/matthewwithanm/react-controllables","last_synced_at":"2025-04-06T03:08:00.173Z","repository":{"id":15584892,"uuid":"18320633","full_name":"matthewwithanm/react-controllables","owner":"matthewwithanm","description":"Easily create controllable components","archived":false,"fork":false,"pushed_at":"2015-08-17T22:12:29.000Z","size":537,"stargazers_count":256,"open_issues_count":2,"forks_count":10,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-30T01:11:52.962Z","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":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/matthewwithanm.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":"2014-04-01T07:01:56.000Z","updated_at":"2023-06-13T01:42:51.000Z","dependencies_parsed_at":"2022-09-08T00:10:23.742Z","dependency_job_id":null,"html_url":"https://github.com/matthewwithanm/react-controllables","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthewwithanm%2Freact-controllables","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthewwithanm%2Freact-controllables/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthewwithanm%2Freact-controllables/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matthewwithanm%2Freact-controllables/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/matthewwithanm","download_url":"https://codeload.github.com/matthewwithanm/react-controllables/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247427006,"owners_count":20937201,"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-07-31T03:00:59.657Z","updated_at":"2025-04-06T03:08:00.142Z","avatar_url":"https://github.com/matthewwithanm.png","language":"JavaScript","funding_links":[],"categories":["Uncategorized","Awesome React","Code Design"],"sub_categories":["Uncategorized","Tools","Data Store"],"readme":"react-controllables\n===================\n\nEasily create controllable components\n\nIf you've worked with forms in ReactJS, you're probably familiar with the idea\nof [controlled and uncontrolled components][1]. Put simply, controlled\ncomponents have their state controlled by another component whereas uncontrolled\ncomponents manage their own state. It turns out that this idea can be really\nuseful for custom components too.\n\n\nUse Case\n--------\n\nImagine you've designed a TabBar component. When you click on a tab, it becomes\nselected and the other tabs in the bar become deselected. The selected tab is\nstored as state in the component. (As a pleasant side-effect, this makes it nice\nand easy to demo in isolation.)\n\nEverything is fine until one day when the designer of your site decides to add\nanother component to the page that also changes the selected tab. Now you've got\na problem: you need to hoist the state to a higher level so it can be shared\nbetween the two components.\n\nInstead of ripping out the state management from your TabBar component (which\nwould make it impossible to play with the component on a page by itself), make\nit *controllable*.\n\n\nHow\n---\n\n1. Write a \"dumb\" component that doesn't manage its state at all but accepts one\n   or more props and corresponding `onPROPNAMEChange` callbacks.\n2. Use `controllable` to create a higher-order component from the dumb one.\n\n\u003csmall\u003e\u003ci\u003e\n  Note: There's one exception to the `onPROPNAMEChange` convention: if the prop\n  name is \"value,\" the callback will be simply \"onChange\". This is done to match\n  the conventions in React itself.\n\u003c/i\u003e\u003c/small\u003e\n\n\u003csmall\u003e\u003ci\u003e\n  Note: react-controllables used to be implemented with a mixin and have a\n  different (more complicated!) usage. The mixin is still included at\n  \u003ccode\u003ereact-controllables/mixin\u003c/code\u003e (or \u003ccode\u003eControllables.Mixin\u003c/code\u003e in\n  the standalone build) but deprecated. Switch!\n\u003c/i\u003e\u003c/small\u003e\n\n\nExample\n-------\n\nWe'll use our TabBar example and represent the selection as an integer using the\nselectedTabIndex prop.\n\n```jsx\nclass TabBar extends React.Component {\n\n  render() {\n    var selectedTabIndex = this.props.selectedTabIndex;\n    return (\n      \u003cul onClick={ this.handleClick.bind(this) }\u003e\n        \u003cli className={ selectedTabIndex == 0 ? 'selected' : '' }\u003eTab Zero!\u003c/li\u003e\n        \u003cli className={ selectedTabIndex == 1 ? 'selected' : '' }\u003eTab One!\u003c/li\u003e\n        \u003cli className={ selectedTabIndex == 2 ? 'selected' : '' }\u003eTab Two!\u003c/li\u003e\n      \u003c/ul\u003e\n    );\n  }\n\n  handleClick(event) {\n    // Call the `onSelectedTabIndexChange` callback with the new value.\n    if (!this.props.onSelectedTabIndexChange) return;\n    var el = event.target;\n    var index = Array.prototype.indexOf.call(el.parentNode.children, el);\n    this.props.onSelectedTabIndexChange(index);\n  }\n\n}\n\nTabBar.defaultProps = {selectedTabIndex: 0};\n\nTabBar.propTypes = {\n  selectedTabIndex: PropTypes.number.isRequired,\n  onSelectedTabIndexChange: PropTypes.func,\n};\n```\n\nNext, use the controllable util to create a higher-order component, telling it\nwhich props you want to be managed.\n\n```jsx\nimport controllable from 'react-controllables';\nTabBar = controllable(TabBar, ['selectedTabIndex']);\n```\n\nWe now have a TabBar component that can store its own state OR be controlled!\nJust use it like normal:\n\n```jsx\n\u003cTabBar /\u003e\n```\n\nWe can specify a value for it to start with using `defaultPROPNAME`:\n\n```jsx\n\u003cTabBar defaultSelectedTabIndex={ 2 } /\u003e\n```\n\nOr we can make it a *controlled* component and manage the state at a higher\nlevel:\n\n```jsx\n\u003cTabBar\n  selectedTabIndex={ indexFromSomewhereElse }\n  onSelectedTabIndexChange={ handler } /\u003e\n```\n\nUnlike React inputs, components built with react-controllables can't really be\nsaid to be either \"controlled\" or \"uncontrolled\" generally. That's because a\nsingle component can have both controlled and uncontrolled values. For example,\nconsider this variation of our TabBar:\n\n```jsx\nTabBar = controllable(TabBar, ['selectedTabIndex', 'color']);\n```\n\nWe could have both \"selectedTabIndex\" and \"color\" be controlled:\n\n```jsx\n\u003cTabBar selectedTabIndex={ 2 } color=\"blue\" /\u003e\n```\n\nOr neither:\n\n```jsx\n\u003cTabBar defaultSelectedTabIndex={ 2 } /\u003e\n```\n\n(Remember, default values don't make a component controlled, they just set the\ninitial internal state.)\n\nOr only one!\n\n```jsx\n\u003cTabBar color=\"blue\" /\u003e\n```\n\n\nDecorator Support\n-----------------\n\nThe react-controllables API is also designed to work with [JavaScript decorator\nproposal]. Decorators provide a very elegant way to use react-controllables (and\nhigher-order components in general) if you're using a transpiler that supports\nthem, like [Babel 5.0 or greater][2]:\n\n```jsx\n@controllable(['selectedTabIndex', 'color'])\nclass TabBar extends React.Component {\n\n  // [SNIP] Body same as above.\n\n}\n```\n\n\n[1]: http://facebook.github.io/react/docs/forms.html#controlled-components\n[JavaScript decorator proposal]: https://github.com/wycats/javascript-decorators\n[2]: http://babeljs.io/blog/2015/03/31/5.0.0/#stage-1:-decorators\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthewwithanm%2Freact-controllables","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatthewwithanm%2Freact-controllables","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthewwithanm%2Freact-controllables/lists"}