{"id":19806613,"url":"https://github.com/timnew/reactx","last_synced_at":"2025-05-01T07:31:00.082Z","repository":{"id":32093377,"uuid":"35665500","full_name":"timnew/reactx","owner":"timnew","description":"A React.js Extension Library, add missing features form React.js","archived":false,"fork":false,"pushed_at":"2015-06-15T07:57:26.000Z","size":176,"stargazers_count":17,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-21T03:05:58.228Z","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/timnew.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":"2015-05-15T09:26:03.000Z","updated_at":"2018-03-21T13:55:01.000Z","dependencies_parsed_at":"2022-09-11T12:51:46.636Z","dependency_job_id":null,"html_url":"https://github.com/timnew/reactx","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timnew%2Freactx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timnew%2Freactx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timnew%2Freactx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timnew%2Freactx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timnew","download_url":"https://codeload.github.com/timnew/reactx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251840160,"owners_count":21652292,"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-11-12T09:08:00.786Z","updated_at":"2025-05-01T07:30:59.836Z","avatar_url":"https://github.com/timnew.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"ReactX [![NPM version][npm-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Dependency Status][depstat-image]][depstat-url]\n================\n\n\u003e ReactX is a [React.js] extension library, add missing features form React.js\n\n## Install\n\nInstall using [npm][npm-url].\n\n    $ npm install reactx --save\n\n## React Compatibility\n\nReactX is designed to be fully backward compatible with React, which can be used as drop in replacement of React without any code change.\n\nAll neat features are included automatically.\n\n```js\n// import React from 'react';\nimport React from 'reactx';\n\n// No code need to be updated\nclass MyComponent extends React.Component {\n  ...\n}\n```\n\n## ReactX.Component\n\n`ReactX.Component` is a ES6 style Component base class, bundled with wanted features.\n\n```js\nimport React from 'reactx';\n\nclass MyComponent extends React.Component {\n  render() {\n    return \u003cspan\u003eReactX Component is cool\u003c/span\u003e;\n  }\n}\n```\n\n### Immutable state `this.data`\n\nReactX componet built in some methods to make it easier to use [immutable.js] as state in `Component`.\n\nIt follows the [approach](https://github.com/facebook/immutable-js/wiki/Immutable-as-React-state) that recommended by Facebook.\n\nTo distinguish the immutable state from normal ReactX state, the new property is called `data`.\n\n```js\nrender() {\n  return \u003cspan\u003e{this.data.get('text')}\u003c/span\u003e;\n}\n```\n**CAUTION**  \nUnlike `this.state`, `this.data` is not readonly, following code cause error.\n```js\nthis.data = {text: 'new Text'};\n```\n\n**HINT**  \nData and its children are immutable objects, which might not be accepted by other components or libraries. You might need to call `this.data.toJS()` to convert it to normal object.\n\n```js\nrender() {\n  return \u003cChildComponent {...this.data.toJS()} className={this.data.cssClasses.toJS()} /\u003e;\n}\n```\n\n#### Initialize data with constructor\n\nThe data can be initialized by calling super's constructor.\n\nThe value provided will be converted into immutable object automatically.\n\n```js\nimport React from 'reactx';\n\nclass MyComponent extends React.Component {\n  constructor(props) {\n    super(props, {\n      text: 'Text initialized'\n    });\n  }\n\n  render() {\n    return \u003cspan\u003e{this.data.get('text');}\u003c/span\u003e;\n    // renders \u003cspan\u003eText initialized\u003c/span\u003e\n  }\n}\n```\n\n**HINT**  \nThe second parameter is optional, `this.data` will be initialized with `{}` if nothing provided.\n```js\nimport React from 'reactx';\n\nclass MyComponent extends React.Component {\n  constructor(props) {\n    super(props);\n  }\n\n  render() {\n    return \u003cspan\u003e{JSON.stringify(this.data.toJS())}\u003c/span\u003e;\n    // renders \u003cspan\u003e{}\u003c/span\u003e\n  }\n}\n```\n\n**CAUTION**  \nIf build your own abstract component based on `ReactX.Component`, make sure you constructor keep the following signature.\n\n```js\nimport React from 'reactx';\n\nclass BaseComponent extends React.Component {\n  constructor(props, initData={}) {\n  }\n}\n```\n\n#### Initialize data with `initData`\n\nBesides the constructor, the data can also by initialized by calling `initData` method.\n\nThe value provided will be converted into immutable object automatically.\n\n```js\nclass MyComponent extends React.Component {\n  constructor(props) {\n    super(props);\n  }\n\n  componentDidMount() {\n    this.initData({\n      text: 'Text initialized'\n    });\n  }\n\n  render() {\n    return \u003cspan\u003e{this.data.get('text');}\u003c/span\u003e;\n    // renders \u003cspan\u003eText initialized\u003c/span\u003e\n  }\n}\n```\n\n**CAUTION**  \nDo not swap usage of `this.updateData` with `this.initData`, although they behaves similar but the actually have quite different implementation. [Here](https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate) explains why.\n\n#### Update Data\n\nSimilar to `setState`, `ReactX.Component` provides `updateData` to manipulate data.\n\n**CAUTION**  \nAlways update data or state by `updateData` or `setState` methods, or you might break React's life cycle management.\n\n```js\n// this.data = {}\nthis.updateData({ name: 'Tim', hobbies: ['coding', 'coding', 'coding'] }); // Set a whole new value\n// this.data = { name: 'Tim', hobbies: ['coding', 'coding', 'coding'] }\nthis.updateData({ name: 'Suca' }); // Replace previous value with new one\n// this.data = { name: 'Suca' }\nthis.updateData(data=\u003edata.set('hobbies', ['relics'])); // Quick update previous\n// this.data = { name: 'Suca', 'hobby', 'relics' };\nthis.updateData((data, props) =\u003e { // A complex update\n  let totalScore = props.scores.reduce((sum, score)=\u003esum+score, 0);\n  return data.withMutations(d=\u003ed.mergeIn(['hobbies'], props.newHobbies).set('averageScore', totalScore/props.scores.length));\n});\n```\nFor more available methods, check [immutable.js official document](http://facebook.github.io/immutable-js/docs/#/).\n\n### State Link\n\nWhen dealing with form inputs, React provides [State Link], but it is provides as Mixin, which isn't supported by ES6 syntax.\n\nSo `ReactX.Component` provides `createStateLink` to create state link.\n\n```js\nimport React from 'reactx';\n\nclass MyComponent extends React.Component {\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003cinput type='checkbox' checkedLink={this.createStateLink('featureEnabled', this.toggleFeature)} /\u003e\n        \u003cinput type='text' valueLink={this.createStateLink('name', this.nameChanged)}\n      \u003c/div\u003e\n    );\n  }\n\n  get featureEnabled() {\n    return this.data.get('featureEnabled');\n  }\n  toggleFeature(value) {\n    this.updateData(data=\u003edata.set('featureEnabled', value));\n  }\n\n  get name() {\n    return this.data.get('name');\n  }\n  nameChanged(name) {\n    this.updateData(data=\u003edata.set('name', name));\n  }\n}\n```\n\n**HINT** `createStateLink` binds `this` for you automatically. So you don't need to worry about the `this`.\n\n#### Deal with value group\n\nWhen deal with input group, usually it shares same change handler across inputs.\nSo in the `handler`, it is important to know which input triggers the event.\n\nBy specify the 3rd parameter of `createStateLink` to true, enable `ReactX.Component` to pass `property name` to callback.\n\n```js\nimport React from 'reactx';\n\nclass MyComponent extends React.Component {\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003cinput type='checkbox' checkedLink={this.createStateLink('featureA', this.toggleFeature, true)} /\u003e\n        \u003cinput type='checkbox' checkedLink={this.createStateLink('featureB', this.toggleFeature, true)} /\u003e\n        \u003cinput type='checkbox' checkedLink={this.createStateLink('featureC', this.toggleFeature, true)} /\u003e\n        \u003cinput type='checkbox' checkedLink={this.createStateLink('featureD', this.toggleFeature, true)} /\u003e\n      \u003c/div\u003e\n    );\n  }\n\n  toggleFeature(featureName, value) {\n    // featureName corresponding to the first value passed to createStateLink.\n    this.updateData(data=\u003edata.setIn(['features', featureName], name));\n  }\n}\n```\n\n### Pure Render Component\n\n[Pure Render] component usually means:\n\n* Clean design\n* Testability\n* Performance Boost\n\nBut to make a component Pure Render isn't that easy in ES6 style syntax. So `ReactX.Component` provides `enablePureRender` static method.\n\n```js\nimport React from 'reactx';\n\nclass MyPureRenderComponent extends React.Component {\n  ...\n}\nMyPureRenderComponent.enablePureRender();\n```\nUnder the hood, `enablePureRender` method wraps up [react-pure-render], make it more accessible without relaying on ES7 draft syntax and mixin.\n\n### React Router integration\n\nAgain, [react-router] is a popular router solution, which provides clean syntax to add routing mechanism to react app.\nBut some of its feature heavily depends on Mixin, which is not available in ES6 syntax.\n\nSo `ReactX.Component` provides `enableReactRouter` utility method to solve the issue. It expose `router` property to access `react-router` instance.\n\n```js\nimport React from 'reactx';\n\nclass MyComponent extends React.Component {\n  onLogOutButtonClicked() {\n    this.router.transitionTo('logout');\n  }\n}\nMyComponent.enableReactRouter();\n```\n\n**HINT**  \nIf you have already declared `router` property on your Component, you can ask `ReactX.Component` to expose `react-router` with a different property.\n\n```js\nimport React from 'reactx';\n\nclass MyComponent extends React.Component {\n  get router() {\n    return this.data.get('serverRouter');\n  }\n\n  onLogOutButtonClicked() {\n    this.reactRouter.transitionTo('logout'); // Access react-router via reactRouter instead of default router\n  }\n}\nMyComponent.enableReactRouter('reactRouter');\n```\n\n### MixIn\n\nAlthough MixIn is not supported by ES6 syntax, and which can be replace with High Order Component. But due to a lot of existing libraries are strongly depends on Mixin. So it is import to have it.\n\n`ReactX.Component` provides `includeSimpleMixIn` to enable developer mount existing mixin onto ES6 style Component with some limitation.\n\n#### Simple MixIn\n\nThe major criticism on Mixin focused on it messing up the life-cycle callbacks and state/property initialization. Except that, mixin is still the most convenient way to abstract concerns.\n\n`ReactX.Component` support mixin with some tradeoff. It doesn't merge the state/property initialization automatically. It also doesn't dispatch life cycle to multiple method automatically. It leaves all these stuff to developer, enable developer to have a better control on the code.\n\n```js\nimport React from 'reactx';\n\nconst SimpleMixIn = {\n  get averageScore() {\n    return this.scores.reduce((sum, score) =\u003e sum + score, 0) / this.scores.length;\n  }\n}\n\nclass MyComponent extends React.Component {\n  get scores() {\n    return this.data.get('scores');\n  }\n\n  render() {\n    return \u003cspan\u003e{this.averageScore()}\u003c/span\u003e;\n  }\n}\n\nMyComponent.includeSimpleMixIn('SimpleMixIn');\n```\n\n#### getInitialState, getDefaultProps, propTypes, contextTypes\n\nTODO\n\n#### Prefix and Lifecycle callbacks\n\nTODO\n\n#### Name Conflict\n\nTODO\n\n## ReactX.PropertyBinder\n\nIn Flux architecture application, to consume `Store` data in `Component` isn't a straightforward as it sounds. Different Flux Implementations provides different approaches.\n\nWhen you try to apply these solutions, you might quickly find:\n\n* They require modifications to my Component\n* They depend on MixIns, which is not supported by ES6 style Component\n* They creates wrapper class\n* The connection between Component and Store are hidden\n* They add additional dependencies to my Component\n* They add external state to my Component, which makes it not a [Pure Render] any more.\n* They make my Component hard to test\n* They make my Component hard to reuse\n* They just don't feel right to me!\n\nIf you feel disappointed about the solutions, `PropertyBinder` might be what you're looking for.\n\n### Property Binding\n\nSuppose I have a `RoleList` component that renders a list of roles, and which is a pure render component.\nSo I can easily using [React Dev Tools] play with it.\n\n```html\n\u003cRoleList playerCount={RoleConfigStore.playerCount}\n          roleCount={RoleConfigStore.roleCount}\n          roleSchema={RoleConfigStore.roleSchema}\n          error={RoleConfigStore.validationError} /\u003e\n```\n\nWhen finished the Component, to connect it to `RoleConfigStore`, it can be done as following code describes\n\n```html\n\u003cPropertyBinder source={RoleConfigStore}\n                binding={{\n                  playerCount: 'playerCount',\n                  roleCount: 'roleCount',\n                  roleSchema: 'roleSchema',\n                  error: 'validationError'\n              }}\u003e\n  \u003cRoleList/\u003e\n\u003c/PropertyBinder\u003e\n```\nIt bounds `RoleConfigStore` state to RoleList's property lists. And the binding relation can be express in HTML syntax.\nWhen `RoleConfigStore` updated, the RoleList proper will updated correspondingly.\n\n### Binding to all kind of sources\n\n`PropertyBinder` doesn't require the type of source, theoretically it works with all kind of source/stores. It even works with plain objects without auto-refresh.\n\n`PropertyBindiner` will try to hook `addChangeListener` on object on source, but this behavior can be overrode by specify `listenerHook` on `PropertyBinder`.\n\n```html\n// Bound to RoleConfigStore with listenTo method\n\u003cPropertyBinder listenerHook=\"listenTo\"\n                source={RoleConfigStore}\n                binding={{\n                  playerCount: 'playerCount',\n                  roleCount: 'roleCount',\n                  roleSchema: 'roleSchema',\n                  error: 'validationError'\n                }}\u003e\n  \u003cRoleList/\u003e\n\u003c/PropertyBinder\u003e\n```\n```html\n// Disable Property Update, and bind to plain object\n\u003cPropertyBinder listenerHook={false}\n                source={{\n                  playerCount: 1,\n                  roleCount: 1,\n                  roleSchema: { name: 'Admin' },\n                  validationError: null\n                }}\n                binding={{\n                  playerCount: 'playerCount',\n                  roleCount: 'roleCount',\n                  roleSchema: 'roleSchema',\n                  error: 'validationError'\n                }}\u003e\n  \u003cRoleList/\u003e\n\u003c/PropertyBinder\u003e\n```\n\n### Bind to multiple source\n\n`PropertyBinder` also support multiple sources, when you specify `multiSource`\n\n```html\n\u003cPropertyBinder multiSource\n                source={{UserStore, RoleConfigStore}}\n                binding={{\n                  UserStore: { isUsersValid: 'isValid' },\n                  RoleConfigStore: { isRoleConfigValid: 'isValid' }\n                }}\u003e\n  \u003cControlPanel /\u003e\n\u003c/PropertyBinder\u003e\n\n// Previous code applies following binding to ControlPanel\n\n\u003cControlPanel isUsersValid={UserStore.isValid}\n              isRoleConfigValid={RoleConfigStore.isValid} /\u003e\n```\n\n## License\nMIT\n\n[![NPM downloads][npm-downloads]][npm-url]\n\n[homepage]: https://github.com/timnew/reactx\n\n[npm-url]: https://npmjs.org/package/reactx\n[npm-image]: http://img.shields.io/npm/v/reactx.svg?style=flat\n[npm-downloads]: http://img.shields.io/npm/dm/reactx.svg?style=flat\n\n[ci-url]: https://drone.io/github.com/timnew/reactx/latest\n[ci-image]: https://drone.io/github.com/timnew/reactx/status.png\n\n[depstat-url]: https://gemnasium.com/timnew/reactx\n[depstat-image]: http://img.shields.io/gemnasium/timnew/reactx.svg?style=flat\n\n[React.js]: https://facebook.github.io/react/index.html\n[immutable.js]: http://facebook.github.io/immutable-js/\n[Pure Render]: https://facebook.github.io/react/docs/pure-render-mixin.html\n[react-pure-render]: https://github.com/gaearon/react-pure-render\n[React Dev Tools]: https://github.com/facebook/react-devtools\n[react-router]: https://github.com/rackt/react-router\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimnew%2Freactx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimnew%2Freactx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimnew%2Freactx/lists"}