diff: (Tree, Tree) → ∆Tree
render() {
return <div>Hello World, at {new Date().toString()}</div>;
}
⟱ JSX Transformer ⟱
render() {
return React.createElement('div', {},
'Hello World, at ', new Date().toString());
}
class App extends React.Component {
render() {
return (
<div className="main">
<div className="picture-bg">
<img src={getRandomBg()} />
</div>
<Clock />
<CmdLine ps1="λ" />
<LinksBox />
</div>
);
}
}
class Clock extends React.Component {
render() {
let time = moment().format(this.props.format);
return (<time>{time}</time>);
}
}
// ...
render() {
return (<div><Clock format="HH:mm" /></div>);
}
// ...
extends
*export let LookMaNoState = (props) => {
return (<span>{props.name}</span>);
}
React.renderDom(<LookMaNoState name="Mark">, mountNode);
setState()
?componentWillReceiveProps
class ProductInfiniteScroller extends React.Component {
scrollHander() {...}
dataLoader() {...}
reposition() {...}
removeClippedSubviews() {...}
render() {
return (
<ul>
{this.state.item.map((i) => {
return (<span>{i.name}</span>);
})}
</ul>
);
}
}
ScrollView
<ScrollView
horizontal={true|false}
bounce={true|false}
paging={true|false}>
</ScrollView>
<ProductPrice data={data} />
<ProductPrice
listPrice={data.price.list}
markdownPrice={data.price.markdown} />
class MyComponent extends React.Component {
onClickHandler() {
this.props.user.likes += 1;
}
render() {
// ...
}
}
class MyComponent extends React.Component {
onClickHandler() {
this.props.onLike(user.props.likes + 1);
}
render() {
// ...
}
}
Record
import {Record} from 'immutablejs';
let FooBar = Record({foo: 'baz', bar: 'blub'});
// ...
let fb = new FooBar({foo: 'lorem'});
fb.get('foo') // -> 'lorem'
let changed = fb.set('foo', 'bar');
fb.get('foo') === changed.get('foo') // -> false
constructor
is an anti-patternclass MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: props.data
}
}
}
Use the prop directly if you need to!
componentWillReceiveProps
to update state when props changedclass MyComponent extends React.Component {
componentWillReceiveProps(nextProps) {
this.setState({
data: nextProps.data
});
}
}
You cannot call setState
in componentWillUpdate
!
MyComponent.propTypes = {
someString: React.PropTypes.string,
someEnum: React.PropTypes.oneOf(['A', 'B']),
someArrayOf: React.PropTypes.arrayOf(React.PropTypes.number)
}
cloneElement
when modifying a child component<Slider images={[....]} />
<Slider>
<Image />
<Image />
<Image />
</Slider>
Bad:
class RxButton extends React.Component {
render() {
return (
<TouchableHightlight>
{this.props.children}
</TouchableHightlight>
);
}
}
<RxButton accessibilityLabel="button">
// ...
</RxButton>
Good:
class RxButton extends React.Component {
render() {
return (
<TouchableHightlight
accessibilityLabel={this.props.accessibilityLabel}>
{this.props.children}
</TouchableHightlight>
);
}
}
<RxButton accessibilityLabel="button">
// ...
</RxButton>
class RxButton extends React.Component {
render() {
return (
<RxButton {...this.props}>
<RxIcon src="..." />
</RxButton>
);
}
}
<RxIconButton accessibilityLabel="button">
// ...
</RxIconButton>
context
*“Using context makes your components more coupled and less reusable”
context
?class MyWrapper extends React.Component {
getChildContext() {
return {
mainColor: '#bada55'
};
}
}
class MyButton extends React.Component {
render() {
return (
<button style={{backgroundColor: this.context.mainColor}}>
{this.props.children}
</button>
);
}
}
console.log(this.context);
// => {}
class MyWrapper extends React.Component {
getChildContext() {
return {
mainColor: '#bada55'
};
}
}
MyWrapper.childContextTypes = {
mainColor: React.PropTypes.string
};
class MyButton extends React.Component {
render() {
return (
<button style={{backgroundColor: this.context.color}}>
{this.props.children}
</button>
);
}
}
MyButton.contextTypes = {
mainColor: React.PropTypes.string
};
class MyWrapper extends React.Component {
getChildContext() {
return this.state;
}
constructor(props) {
super(props);
UserSerivce.getUserByRoute(this.context.currentRoute)
.then((user) => {
this.setState({user});
});
}
render() {
return (<Profile />);
}
}
assume contextTypes
are set correctly
class Profile extends React.Component {
// ... addAsFriend()
render() {
return (
<div className="user-profile">
<h3>{this.context.user.name}</h3>
<span>
{`Friends: ${this.context.user.friendCount}`}
</span>
<button onClick={this.addAsFriend.bind(this)}>
Add As Friend
</button>
</div>
);
}
}
class Profile extends React.Component {
addAsFriend() {
this.context.user
.addUser(this.context.currentUser);
}
// ... render()
}
class MyWrapper extends React.Component {
constructor(props) {
super(props);
}
// data retrieval with Flux
render() {
return (<Profile user={this.state.user} />);
}
}
class Profile extends React.Component {
// ... addAsFriend()
render() {
return (
<div className="user-profile">
<h3>{this.props.user.name}</h3>
<span>
{`Friends: ${this.props.user.friendCount}`}
</span>
<button onClick={this.addAsFriend.bind(this)}>
Add As Friend
</button>
</div>
);
}
}
addAsFriend()
with Flux
<input ref="username" type="text" value={this.props.username} />
// ...
console.log(this.refs.username)
// => HTMLElement
<MaterialTextInput ref="textinput" value={this.props.value} />
// ...
console.log(this.refs.textinput)
// => MaterialTextInput
console.log(ReactDOM.findDOMNode(this.refs.textinput))
// => HTMLElement
class RefTest extends React.Component {
render() {
return (
<input
ref={(i) => { this.input = i; }}
type="text"
value={this.props.username} />
);
}
}
null
as argument, when component is unmounted
The componentDidMount()
method of child components is invoked before that of parent components
import ReactTestUtils from 'react-addons-test-utils'
isElement(element)
isElementOfType(element, ComponentClass)
findAllInRenderedTree(tree, testFn)
scryRenderedComponentsWithType(tree, ComponentClass)
findRenderedComponentWithType(tree, ComponentClass)
import {isElementOfType, isElement} from 'react-addons-test-utils';
test('existence', () => {
expect(isElement(<MyComponent />)).to.be.true;
expect(isElementOfType(<MyComponent />, MyComponent)).to.be.true;
});
Source
let shallowRenderer;
setup(() => {
shallowRenderer = createRenderer();
});
test('ensure correct sub components', () => {
shallowRenderer.render(<MyComponent />)
let output = shallowRenderer.getRenderOutput();
expect(
isElementOfType(output.props.children, MySubComponent)
).to.be.true;
});
Source
var jsdom = require('jsdom');
var document = jsdom.jsdom('<!doctype html><html><body></body></html>');
var window = document.defaultView;
global.document = document;
global.window = window;
for (var key in window) {
if (!window.hasOwnProperty(key)) {
continue;
} else if (key in global) {
continue;
} else {
global[key] = window[key]
}
}
renderIntoDocument(componentInstance)
import {Simulate} from 'react-addons-test-utils';
let node = renderIntoDocument(<MyComponent />);
function renderIntoDocument(instance) {
var div = document.createElement('div');
return ReactDOM.render(instance, div);
}
Nothing is actually rendered into the document.
This is great for test isolation!
findRenderedDOMComponentWithTag(node, tagName)
import {findRenderedDOMComponentWithTag} from 'react-addons-test-utils';
findRenderedDOMComponentWithTag(node, 'button');
// returns DOM node or throws error
findRenderedDOMComponentWithTag(node, tagName)
import {findRenderedComponentWithType} from 'react-addons-test-utils';
findRenderedComponentWithType(node, MySubComponent);
// returns instance node or throws error
// only works with fully 'rendered' nodes (not shallowly rendered)
import {Simulate} from 'react-addons-test-utils';
Simulate.click(clickableNode);
// clickableNode is 'real' DOM node that has a onClick event,
// i. e. <button>
Simulate.change(inputNode);
Simulate.keyDown(inputNode, {key: 'Enter', keyCode: 13});
// Simulate.{eventName}(node, eventData);
// for every event that React supports
clickHandler() {
this.setState({hide: true});
}
render() {
if (this.state.hide) {
return (<div></div>);
} else {
return (
<div>
<MySubComponent
name="testing"
onClick={this.clickHandler.bind(this)} />
</div>
);
}
}
test('simulate click', () => {
let node = renderIntoDocument(<MyComponent />);
Simulate.click(findRenderedDOMComponentWithTag(node, 'button'));
expect(findRenderedDOMComponentWithTag(node, 'div')).to.be.defined;
expect(
findRenderedDOMComponentWithTag
.bind(undefined, node, 'button')
).to.throw(Error);
});
Source
test('simulate click', () => {
let node = renderIntoDocument(<MyComponent />);
Simulate.click(findRenderedDOMComponentWithTag(node, 'button'));
expect(findRenderedDOMComponentWithTag(node, 'div')).to.be.defined;
expect(
findRenderedDOMComponentWithTag
.bind(undefined, node, 'button')
).to.throw(Error);
});
Source
accessibilityLabel
-prop liberallyaccessible={true}
-prop where appropriate
What does accessible={true}
mean?
<View>
<Slider>
<Image resizeMode='contain' source={{uri: images[0]}} />
<Image resizeMode='contain' source={{uri: images[1]}} />
<Image resizeMode='contain' source={{uri: images[2]}} />
</Slider>
</View>
<ScrollView
accessibilityLabel="slider"
horizontal={true}
...>
{elements}
</ScrollView>
let slider = XCUIApplication().otherElements["slider"]
slider.swipeLeft()
slider.swipeRight()
slider.swipeLeft()
slider.swipeLeft()
XCTAssert(slider.images["image 3"].frame == slider.frame)
Requires 2 parts:
All Exceptions should be thrown in JS
import {expect} from 'chai';
import React from 'react-native';
let {AppRegistry, View, Component} = React;
import {TestModule} from 'NativeModules';
class TestComp extends Component {
render() {
TestModule.markTestCompleted();
return (<View></View>);
}
}
AppRegistry
.registerComponent('ExampleTests', () => { return TestComp });
@implementation ExampleTests
{
RCTTestRunner *_runner;
}
- (void)setUp
{
[super setUp];
_runner = RCTInitRunnerForApp(@"intTestDist/tests.int", nil);
}
- void()testExampleTests
{
[_runner runTest:_cmd module:@"ExampleTests"]
}
@end