Getting started
Overview
React-MVx is the MVVM JS application framework built upon three technologies:
- React as a view layer.
- Type-R as a data layer and UI state management.
- NestedLink for two-way data binding.
Contrary to the popular React approaches, React-MVx does not try to avoid the distributed mutable application state. Instead, it is focused on bringing stateful components capabilities to their maximum.
React-MVx is built around the idea of universal state management featuring the same technique to manage the local component state, application page state, and the global application state. Core building blocks of the application architecture are:
- React-MVx Component (extended
React.Component) for the view layer. - Records and Collection (provided by Type-R data framework) for managing all kinds of an application state.
- Links for two-way data binding.
- Stores (which is the subclass of the Record and can be dynamically created) for resolving record's id-references.
React-MVx Component uses the Record class to manage its local state. Record can consists of other records and collections, describing the data structure of arbitrary complexity. All records are serializable by default, has deeply observable changes, and supports the declarative validation. The behavior of record's attributes and component state/props elements is controlled with declarative type annotations.
React-MVx extends React namespace and should be used instead of react.
All class Component definitions must be preceeded with the @define decorator.
import React, { define } from 'react-mvx'
@define class HelloApp extends React.Component {
static state = {
count : 0
};
render(){
const { state } = this;
return (
<h1 onClick={ () => state.count++ }>
Hi there! { state.count }
</h1>;
);
}
}
Key features
- Run-time type asserts. Application state defined with Type-R is protected from improper assignments, including incompatible JSON coming from the network.
- Speed. Collections of 10-20K items are being parsed and processed with sub-second delays.
- Complex state support. Component's state is described with Type-R Record and its attributes can be composed of complex types, including other records and collections with indefinite nesting.
- Automatic serialization. All state elements are serializable to JSON by default with no additional effort.
- id-references support. References by object ids in JSON are resolved automatically, including the situation with multiple collections of records holding cross-references which are being fetched independently.
- Deeply observable changes. Updates are executed in transactions allowing the bunch of changes to be made as reactions on other changes, without introducing extra change events and renders.
- Mutable data structures with pure render support. For some reason, many people believes that it's impossible.
- Two-way data binding. Dealing with complex forms is the strong side of the React-MVx.
- Forms validation. You'll get it for free, just attach the check to the state's attribute.
- Layered application state. You can create and dispose as many stores as you want dynamically. In many cases, you don't even need the store - there's no any difference in managing the component's local state and the store.
- MVVM pattern support. MVVM scales perfectly, and we believe its support is a must.
Examples
Babel + Webpack boilerplate
The place to start your project.
Checklist tree
Hierarchical checklist with simple group selection rules. Demonstrates:
- recursive data structures
- deeply observable changes
- change even watchers and transactional changes
- pure render on mutable data
TodoMVC
Classic TodoMVC example.
UsersList
Editable list of users. Demonstrates the state synchronization pattern.
Flux Comparison
Detailed design of the "flux comparison" example with explanation of the unidirectional data flow.
Installation & Requirements
Supported browsers: Chrome, Firefox, Safari, IE10+.
Requires react, prop-types, and type-r as peer dependencies. Installation (assuming that React is installed):
npm install react react-mvx type-r prop-types --save-dev
The best way to start is to use the boilerplate which already has babel, webpack, and the minimal set of required dependencies configured.
TypeScript is unsupported (yet) but may work.
Developer tools
The modified version of the React Developer Tools with fixes required for React-MVx is available in the Chrome Web Store.
- Fixed an exception when inspecting the props with custom getters in prototype.
- Collections are displayed as an array (system fields hidden).
- Record is displayed as an object (system fields hidden).
- Component's state (which is the Record) is displayed as an object.
Design patterns
Stateful Component
Inline state declaration
@define class Test extends React.Component {
static state = {
counter : 0
}
render(){
const { state } = this;
return (
<div onClick={ () => state.counter++ }>
{ state.counter }
</div>
);
}
}
External state declaration
@define class TestState extends Record {
static attributes = {
counter : 0
}
}
@define class Test extends React.Component {
static State = TestState;
render(){
const { state } = this;
return (
<div onClick={ () => state.counter++ }>
{ state.counter }
</div>
);
}
}
Transactional state changes
@define class Test extends React.Component {
static state = {
counter1 : 0,
counter2 : 0
}
onClick = () => {
this.state.transaction( state => {
state.counter1++;
state.counter2++;
});
}
render(){
const { state } = this;
return (
<div onClick={ this.onClick }>
{ state.counter1 + state.counter2 }
</div>
);
}
}
I/O
Persistent UI state
@define class TestState extends Record {
static endpoint = localStorageIO( 'myApp' );
static attributes = {
id : 'TestPage'
counter : 0
}
}
@define class TestPage extends React.Component {
static State = TestState;
componentWillMount(){
this.state.fetch();
}
componentWillUnmount(){
this.state.save();
}
render(){
const { state } = this;
return (
<div onClick={ () => state.counter++ }>
{ state.counter }
</div>
);
}
}
Initial loading indicator...
@define class Page extends React.Component {
static state = {
someCollection : Collection,
loading : true
}
onLoad = () => this.state.loading = false;
componentWillMount(){
this.state.collection.fetch()
.then( onLoad ).catch( onLoad );
}
render(){
const { state } = this;
return state.loading ? (
<div>Loading...</div>
) : (
<div>
Loaded { state.someCollection.length } items.
</div>
);
}
}
Component's loading indicator...
@define class List extends React.Component {
static props = {
collection : Collection
}
render(){
const { collection } = this.props;
return collection.hasPendingIO() ? (
<div>Loading...</div>
) : (
<div>
Collection has { collection.length } items.
</div>
);
}
}
Fetching multiple objects...
@define class TestState extends Record {
static endpoint = attributesIO();
static attributes = {
coll1 : Some.Collection,
coll2 : Other.Collection
}
}
@define class TestPage extends React.Component {
static State = TestState;
componentWillMount(){
this.state.fetch();
}
render(){
//...
}
}
Application Examples
Data binding and forms
Tutorial: two-way data binding and forms
In this tutorial, we will learn the state management and two-way data binding basics on the example of the simple user info editing form.
Input Validation
Tutorial: State Definition and Form Validation
In this tutorial, we will add input validation to the user editing form from the previous example. That’s the client-side “as-you-type” validation preventing the user from submitting invalid data while giving him hints.
Editable lists
Illustrates state synchronization pattern.
id-references and stores
Demonstrates local stores and id-references.
Component
React-MVx extends React namespace and should be used instead of react.
All class Component definitions must be preceeded with the @define decorator.
All features of the Component are controlled through the unified property and attribute declarations.
import React, { define } from 'react-mvx'
@define class Hello extends React.Component {
static props = { // instead of `propTypes` and `defaultProps`
propName : `propDef`,
...
}
static context = { // instead of `contextTypes`
propName : `propDef`,
...
}
static childContext = { // instead of `childContextTypes`
propName : `propDef`,
...
}
static state = { // instead of "this.state = " in the constructor.
attrName : `attrDef`,
...
}
static store = { // store
attrName : `attrDef`,
...
}
render(){...}
}
Type annotations summary
The majority of React-MVx features are controlled with declarative props, state, store, and context type annotations.
all (state/store/props/context)
Type annotations below represents the run-time type assertions.
| Annotation | Description |
|---|---|
Ctor |
element has specified type |
Ctor.isRequired |
element is required |
Ctor.has.check(...) |
custom validation check |
state, store, props
You can specify the default value for an attribute or prop, and reactions on its change.
| Annotation | Description |
|---|---|
Ctor.value( defaultValue ) |
element has default value |
defaultValue |
element has default value |
Ctor.has.watcher(...) |
custom reaction on element's change |
Ctor.has.events(...) |
listen to custom events from the element |
Ctor.has.changeEvents(...) |
update on element's changes |
state, store
You have an an attribute-level control of the serialization and ownership for the state, store, and records attributes.
| Annotation | Description |
|---|---|
Record.shared |
attribute holds the reference to the record |
Collection.Refs |
collection of references to the records |
Record.from(...) |
reference to the record from the specified collection |
Collection.subsetOf(...) |
collection of references to the records from the specified collection |
Ctor.has.parse(...) |
custom parse hook |
Ctor.has.toJSON(...) |
custom serialization hook |
Ctor.has.get(...) |
attribute read hook |
Ctor.has.set(...) |
attribute write hook |
Ctor.has.metadata(...) |
attach custom metadata to the attribute |
props
Component static props declaration replaces standard React's propTypes and defaultProps.
static props = { name : propDef, ... }
Declare component props. Declaration is an object
import React, { define } from 'react-mvx'
@define class PropsDemo extends React.Component {
static props = {
name : Declaration,
...
}
}
static pureRender = true
Prevents subsequent render calls in case if props were unchanged. It's known as "pure render" optimization.
Inner changes of records and collections are detected and taken into account. Thus, it works properly with mutable records and collections.
static props declaration is required for pureRender to work. Only declared props will be tracked and compared.
propDef name : Constructor
Checks if component prop is an instance of the Constructor and puts the warning to the console if the prop type is not compatible.
propDef name : Constructor.isRequired
Mark property as required.
import React, { define } from 'react-mvx'
@define class PropsDemo extends React.Component {
static props = {
// Simple form of annotation is just the constructor function designating the type.
optionalArray: Array,
optionalBool: Boolean,
optionalFunc: Function,
optionalNumber: Number,
optionalObject: Object,
optionalString: String,
optionalSymbol: Symbol,
// Anything that can be rendered: numbers, strings, elements or an array
// (or fragment) containing these types.
optionalNode: React.Node,
// A React element.
optionalElement: React.Element,
// You can also declare that a prop is an instance of a class.
optionalMessage: Message,
// You can chain any of the above with `isRequired` to make sure a warning
// is shown if the prop isn't provided.
requiredFunction : Function.isRequired
};
...
}
propDef name : Constructor.value( defaultValue )
Assign default property value.
propDef name : defaultValue
Assign default property value. The the type will be inferred from the defaultValue.
Any function in props annotation is treated as a constructor. Thus, Function.value( defaultValue ) must be used to specify the defaults for functions.
import React, { define } from 'react-mvx'
@define class PropsDemo extends React.Component {
static props = {
withDefault : String.value( 'defaultValue' ),
anotherDefault : 'defaultValue'
}
}
propDef name : Constructor.has.watcher( 'componentMethodName' )
propDef name : Constructor.has.watcher( function( newValue, name ){ ... } )
Watcher is the function which is called when the particular prop is assigned with new value.
Watcher is called after componentWillMount, and may be called during componentWillReceiveProps if the property is changed.
Watcher is executed in the context of the component.
import React, { define } from 'react-mvx'
@define class PropsDemo extends React.Component {
static props = {
first : String.has.watcher( 'onFirstChange' ),
second : Number.has.watcher( second => console.log( 'Received new prop:', second ) )
}
onFirstChange( newValue, name ){
console.log( 'First prop is about to change:', newValue );
}
}
propDef name : RecordOrCollection.has.changeEvents( true )
Observe internal changes of the record or collection and update the component in case of changes.
propDef name : EventSource.has.changeEvents( 'event1 event2 ...' )
Update the component in case if property triggers any of the listed events.
import React, { define } from 'react-mvx'
import { Record } from 'react-mvx'
@define class PropsDemo extends React.Component {
static props = {
// Render on every change
trackInnerChanges : Record.has.changeEvents( true ),
// Render when record is added to or removed from the collection
anotherDefault : Collection.has.changeEvents( 'add remove' )
}
}
propDef name : EventSource.has.events({ event : handler, ... })
Subscribe for events from the component property. handler can either be the name of the component's method,
or the function handling the event. Handler is executed in the context of the component.
state
Component's state is modeled as Type-R Record. Record is created before the call to componentWillMount() and disposed after the call to componentWillUnmount().
All changed inside of the state record are observed, and the component is updated in case of change.
static State = RecordConstructor
Define stateful component with the state Record declared externally.
static state = { name : attrDef, ... }
Inline definition of the state record with a given attributes declaration. All declarations working on props works for the state as well. Refer to the Record documentation for the attributes declaration syntax.
component.state
Holds an object of the Record subclass.
Do not use component.setState(). Use direct assignments to modify the state
this.state.x = 5;
Refer to the Record documentation for the complete list of methods.
component.transaction( fun )
Group the sequence of state (and props) updates in the single transaction leading to single UI update. Read more about transactions in Record's manual.
this.transaction( state => {
state.a++;
state.b++;
this.props.collection.reset();
});
store
Stores in Type-R are internally similar to the Record and used to resolve one-to-many and many-to-many relationships by id. Stores must not be used to store UI state; they are intended to hold the shared domain state which is cross-referenced by id.
There may be multiple stores in Rect-MVx. There is the single default store (Store.global) which is used to cache the data that must be accessible across the pages.
Specifying the store for the top-level component sets this store as the primary one for all the internal state of the current component subtree.
static store = existingStore
Expose the existingStore to the component subtree. Update component on store changes.
static store = StoreConstructor
Creates the local store on component's mount and dispose it when component is unmounted.
Expose the store to the component subtree. Update component on store changes.
static store = { attrName : attrDef, ... }
Implicitly create the Store subclass from the given attribute spec.
Accepts the same attrDef as the state and Record.
component.store
When the static store is defined, provide the access to the store in component.
Store is not directly accessible to the subcomponents; you have to pass values down as props.
context
Static context and childContext declarations replaces React's standard contextTypes and childContextTypes.
static context = { name : propDef, ... }
Replacement for standard contextTypes.
static childContext = { name : propDef, ... }
Replacement for standard childContextTypes.
getChildContext() function is required to create the context as in raw React.
propDef name : Constructor
Checks whenever the value is an instance of the Constructor and puts the warning to the console if the prop type is not compatible.
propDef name : Constructor.isRequired
Value is required.
Methods
component.linkAt( 'key' )
Create the link for the state member key. Same as component.state.linkAt( 'key' ).
component.linkAll()
Create links for all (or specified) the state members. Same as component.state.linkAll().
component.links
Direct access to the links cache. Can be used in event handlers to access the links created during the last render().
All links created for records (and for the component's state) are being cached. They are recreated only in case when their value has been changed.
component.asyncUpdate()
Safe version of the forceUpdate(). Gracefully handles component disposal and UI update transactions.
Shall be used in place of every manual call to forceUpdate().
Events mixin (7)
Component implements Events interface from the Type-R framework, thus it's able to trigger and subscribe for events.
Link
Overview
Link is an intermediate object used to implement the two-way data binding. It acts like a transport for the value, the callback to modify it, its validation error, abstracting out UI controls from the data representation in the state container and from the state container per se.
Links to the record's attributes are commonly created with linkAt and linkAll methods of records.
Record is automatically validated upon the links creation, and link encloses the validation error
related to the particuler attribute.
// Data bound control for the semantic form markup
const Input = ({ valueLink, ...props }) => (
<div className={`form-control ${ valueLink.error ? 'error' : '' }`}>
<input {...props}
value={ valueLink.value }
onChange={ e => valueLink.set( e.target.value ) }
/>
<div className="validation-error">{ valueLink.error || '' }</div>
</div>
);
Links makes it possible to create the semantic markup for the form elements, encapsulating the all the required styles and validation error indication.
Link structure
Here are the most important link members. All the link members are read-only and should not be modified directly.
// Link's shape
{
value : /* the value */,
set( newValue ){ /* the function to change it */},
error : /* validation error */
}
link.value
Holds linked value. This value is immutable.
link.error
Holds the validation error (typically the text error message) which might be consumed and displayed by data-bound countrol.
An error is populated automatically on link creation when using linkAt() or linkAll() methods, and is produced by declarative
validators from .has.check() attributes annotations.
link.set( newValue )
Tells the state container to update the value.
<button onClick={ () => boolLink.set( !boolLink.value ) } />
link.update( prevValue => newValue )
Update link value using the given value transform function.
<button onClick={ () => boolLink.update( x => !x ) } />
Create the link
All records and collections may create links to its elements. Component has the shorthand methods to create links to its state record elements.
You can create custom link object encapsulating complex data binding logic with Link.value.
record.linkAt( attr )
Create the link to the record's attribute. Semantically it's the reference to the attribute.
const nameLink = user.linkAt( 'name' );
collection.linkAt( prop )
Create the link to the custom collection property. Property's setter must modify some record's attributes or change the collection.
component.linkAt( key )
Create the link to the attribute of the conponent's state. Works similar to component.state.linkAt( key ).
record.linkAll()
Link all (or listed) records' attributes.
// Link all attributes...
const { name, email, age } = user.linkAll();
// Link specified attributes...
const { name, email } = user.linkAll( 'name', 'email' );
component.linkAll()
Link all (or listed) attributes of the component's state. Works similar to component.state.linkAll().
collection.linkContains( record )
Create the boolean link which is true whenever the record is contained in the collection.
Setting the link to false will remove record from the collection, setting it to true will add it.
Link.value( value, set )
Create custom link with the given value and set function. Use the link.check method to populate the validation error.
Link transformations
link.equals( value )
Create boolean equality link which value is true whenever link.value === value, and false otherwise.
When an equality link is assigned with true the parent link value is set with value, and with null otherwise.
<Checkbox checkedLink={ stringLink.equals( 'optionX' ) } />
<Checkbox checkedLink={ stringLink.equals( 'optionY' ) } />
Useful for radio groups.
link.props
Converts the link to the standard { value, onChange } props to be easily consumed by standard <input /> control.
<input type="text" {...nameLink.props} />
link.action( ( prevValue, event ) => newValue )
Convert the link to the UI event handler event => void which will transform the link using the given function.
link.action takes transform function, and produce a new function which takes single event argument.
When it's called, event and link value are passes as transform parameters, and link will be updated
with returned value.
// simple click event handler...
<button onClick={ boolLink.action( x => !x ) } />
// manual binding to input control:
const setValue = ( x, e ) => e.target.value;
...
<input value={ link.value }
onChange={ link.action( setValue ) } />
This is particularly useful in (but not restricted to) UI event handlers.
link.check( predicate : value => boolean, errorMsg? )
Checks whenever the predicate is truthy on linked value, and if it's not, assigns link.error with errorMsg.
Does nothing if link.error is already populated.
This method may be used for additional validation in render(), and to populate the validation error for the custom links created with Link.value().
You typically don't need link.check for links created with linkAt() methods, because the validation happens inside of records.
Links to objects and arrays
It's rather unusual scenario that you hold complex raw JS data as a part of your state, because typically the state is defined as a superposition of nested records and collections.
Links can be used to make purely functional updates of the objects and arrays in records attributes. It's done with the help of at-links, which points to the elements of linked objects and arrays.
link.at( key )
Create an at-link to the member of array or object.
Whenever an at-link is updated, it will lead to proper purely functional update (with shallow copying) of the container (array or object).
// Update this.state.array[ 0 ].name
this.linkAt( 'array' ).at( 0 ).at( 'name' ).set( 'Joe' );
link.map( ( itemLink, itemKey ) => any | void )
Map and filter through the linked array or object to produce an array.
Mapping function receives at-link to the corresponding element.
Whenever it returns undefined, the corresponding element is be skipped.
// Render the linked array...
var list = stringArrayLink.map( ( itemLink, index ) => {
if( itemLink.value ){ // Skip empty elements
return (
<div key={ index }>
<Input valueLink={ itemLink } />
</div>
);
}
});
link.update( clonedObject => modifiedObject )
Update linked object or array.
Plain objects and arrays are shallow copied already, thus it's safe just to update the value in place.
// Update the linked object
<button onClick={ () => objLink.update( obj => {
obj.a = 1;
return obj;
}) } />
link.action( ( clonedObject, event ) => Object )
Creates action to update enclosed object or array.
Plain objects and arrays are shallow copied already, thus it's safe just to update the value in place.
link.removeAt( key )
Remove element with a given key from the linked object or array.
atLink.remove()
Remove element with a given key from the linked object or array.
Object-specific methods
link.pick( key1, key2, ... )
Create at-links to the object's members with designated keys, and wrap them in an object.
// Bulk create at-links for the linked object
const { name, email } = objLink.pick( 'name', 'email' );
Array-specific methods
linkToArray.splice() : void
Similar to Array.splice() method, but performs purely functional update.
linkToArray.push() : void
Similar to Array.push() method, but performs purely functional update.
linkToArray.unshift() : void
Similar to Array.unshift() method, but performs purely functional update.
linkToArray.contains( element )
Creates the boolean link to the presence of value in array.
Resulting link value is true whenever element is present in array, and false otherwise.
Whenever resulting link is assigned with new value, it will flip element in the array.
Useful for the large checkbox groups.
Record and Collection
React-MVx relies of Type-R framework to manage a multi-layer application state.
- Record class is used to manage component's state.
- The subset of the Record attributes type annotation is used to define props and context.
- Store class is used to represent component's store (also called "local store"), which is typically used in the root component of SPA page associated with a route. The state of the children components uses upper component's stores to resolve id-references.
- Type-R global store may be used as a store for the data shared by application pages. This store is used to resolve id-references in case if local stores lookup failed.
Data binding
Ad-hoc data binding
{ ...link.props }
<tag { ...link.props } />
Bind the linked value to any standard UI control expecting value and onChange props.
Text controls
Bind input or textarea to the linked string:
// String
<input type="text" {...link.props} />
<textarea {...link.props} />
Checkboxes
Bind the checkbox to the linked boolean:
<input type="checkbox" {...boolLink.props } />
Bind the checkbox to the presence of value in the array:
// array = [ 'optionA' ]
<input type="checkbox" {...arrayLink.contains( 'optionA' ).props } /> // Checked
<input type="checkbox" {...arrayLink.contains( 'optionB' ).props } /> // Unchecked
Bind the checkbox to the presence of the record in the collection:
<input type="checkbox" {...collection.linkContains( record ).props } />
Radio groups
Bind radio group to the single linked value:
// Radio
<input type="radio" {...link.equals( 'optionA' ).props } />
<input type="radio" {...link.equals( 'optionB' ).props } />
Select list
Bind select list to the linked value:
// Select
<select {...link.props}>
<option value="optionA"> A </option>
<option value="optionB"> B </option>
</select>
Linked UI controls
Linked control is the custom React component taking the link property instead of value/onChange props pair.
It uses the link to extract the value and validation error, and to modify the value.
Linked controls makes it possible to create the semantic form markup encapsulating inline validation and form layout styling. Not just form controls, but the most of the UI can benefit of this technique.
// Custom data-bound control
const Input = ({ link, ...props }) => (
<div className={`form-row ${ link.error ? 'has-error' : '' } `}>
<input type="text" {...props} { ...link.props } />
<div className="error-placeholder">{ link.error || '' } </div>
</div>
);
// Another simple data bound control
const Input = ({ link, ...props }) => (
<input {...props}
value={ link.value }
onChange={ e => link.set( e.target.value ) } />
);
There are the set of pre-defined linked UI controls in react-mvx/tags modules. Inline error indication is rather project-dependent, thus this file is intended to be used as a reference and starting boilerplate for your controls.
import { Input } from 'react-mvx/tags'
Text input controls
tags.jsx contains wrappers for standard <input> and <textarea> tags,
which consume linked strings. These wrappers add invalid class to enclosed HTML element if an error is present in the link,
and required class if isRequired validator is the failing one.
import { Input, TextArea } from 'react-mvx/tags'
...
<Input type="text" valueLink={ link } />
<TextArea valueLink={ link } />
Its implementation is rather straightforward.
Numeric input
In some cases you can use the wrong input rejection instead of (or in addition to) the validation. The most popular example is the numeric-only input control. It guarantees that the linked value will only be updated with the valid number, completely encapsulating all related checks and mechanics.
The challenge here is that when number in not an integer it has to go through the sequence of intermediate invalid states during the editing process. Like "" -> "-" -> "-0" -> "-0." -> "-0.5".
The proper implementation of wrong input rejection might be tough.
tags.jsx contains the cross-browser numeric input tag. It has following differences compared to the regular <Input>:
- Keyboard input which obviously leads to invalid values (e.g. letters) is rejected.
- Link value is guaranteed to be the valid number.
- There are
integerandpositiveboolean props controlling input rejection. They can be combined.
<NumberInput> validates its intermediate state and adds invalid class to enclosed input element if it's not a number.
import { NumberInput } from 'react-mvx/tags'
<NumberInput valueLink={ link } />
<NumberInput valueLink={ link } integer={ true }/>
<NumberInput valueLink={ link } positive={ true }/>
Checkboxes
There are different ways how you can handle the checkbox components. The problem of the standard checkbox control, though, is that it's not that easily styled.
tags.jsx contains the custom checkbox and radio components implemented using the plain <div />
element which toggles selected class on click. By default, it has checkbox CSS class,
which can be overridden by passing className prop. It passes through anything else, including children.
<Checkbox checkedLink={ booleanLink } />
<Checkbox checkedLink={ arrayLink.contains( 'option' ) } />
Radio Groups
There are two different ways how you can approach the data binding for the radio groups.
First option is to pass the value of the particular option along with the link. Link this:
<label>
A: <Input type="radio" valueLink={ flagLink } value="a" />
</label>
<label>
B: <Input type="radio" valueLink={ flagLink } value="b" />
</label>
Alternatively, you can use link.equals( value ) method to produce the boolean
link which is specially designed to create radio groups, as it's illustrated by
the custom <Radio /> tags from the tags.jsx.
Internally, it's <div> element which always sets its link to true on click.
And whenever the link value is true, it adds selected class to the div.
By default, it has radio CSS class, which can be overridden by passing className prop.
It passes through anything else, including children.
<label>
A: <Radio checkedLink={ flagLink.equals( 'a' ) } />
</label>
<label>
B: <Radio checkedLink={ flagLink.equals( 'b' ) } />
</label>
Release Notes
2.0.0
- Bug fixes:
- Fixed problems with deep inheritance from the React.Component
- Mixins now works well with inheritance.
- Breaking changes:
- React lifecycle hook methods for the subclass are called automatically.
- Other:
{ ...link.props }for ad-hoc data binding makestags.jsxwrappers obsolete.- Type-R v2.0 is required.
- New universal API for generating links (
linkAt()) - First-class component stores support.