The flavor of JS we use is based on ES5 but with ES6 and ES7 transforms. This means, that a large part of the syntax is based on the current version of JS, but is flavored with capabilities that come with ES6 and ES7. For example:
For more information see React Native JavaScript Environment
Components should be created using ES6 classes and Object Short Notation.
// Bad var ChatBox = React.createClass({ getInitialState: function(){ return {toggle: 'on'}; }, render: function(){ return ( <Text> Example </Text> ); } }) // Good class ChatBox extends React.Component{ constructor(){ this.state = {toggle: 'on'} } render(){ return ( <Text> Example </Text> ); } }
Component functions that are not life-cycle functions should be created with an underscore at the beginning of the function name.
_fetchmessages(){ fetch(requestURL) .then((response)=> response.json() ) .then((responseData)=> { this.setState({messages: responseData}) }) .done(); } _turnEditOn(){ this.setState({edit: true}); }
Keep life-cycle functions at the top of the object constructor in logical order.
constructor
componentWillMount
componentDidMount
componentWillReceiveProps
render
For more information see Component Specs and Lifecycle
Every component function should be as parsimonious as possible. If you find yourself creating a function that is doing more than two things at once, then break up the function into smaller functions.
When passing a function as prop to a child component, the use of bind is not necessary. React Native automatically binds this
to that function.
// Bad render(){ return( <CameraButton selectImage={this.selectImage.bind(this)} /> ); } // Good render(){ return( <CameraButton selectImage={this.selectImage} /> ); }
It necessary however to use bind, when passing a component function to an event prop. For example:
<ListView renderRow={this.renderMessage.bind(this)} /> ... renderMessage(message) { return ( <Message message={message} navigator={this.props.navigator} fetchMessages={this.fetchMessages.bind} /> ); }
Explicitly pass props if the number of props is small or if the prop is important enough that you want to be explicit about it.
If there are a large number of props, pass in the whole thing or use destructuring to break it apart and pass them correctly.
//Bad <MessageBody content=this.props.message.content /> <MessageFooter replies=this.props.message.replies upvotes=this.props.message.upvotes timestamp=this.props.message.timestamp distance=this.props.message.distance /> // Good var {content, ...footer} = this.props.message <MessageBody {content} /> <MessageFooter {..footer} />
Sometimes you want to combine objects and pass them as props. In that case use Object.assign
.
_onPressMessage() { var {message, ...props} = this.props; var {votes, ...message} = this.props.message; var fetchMessages = this._updateHearts; this.props.navigator({ component: Comments, passProps: Object.assign( {..props}, {..message}, {fetchMessages} ), }) }
For more information on passing props, see React Native Transferring Props
Props are immutable. Do not mutate them.
// Bad var component = <Component />; component.props.foo = x; component.props.bar = y;
State is mutable.
constructor(){ this.state = {messages:[]}; } ... _addMessages(data){ var messages = data.messages; this.setState({ messages: this.state.messages.push(messages) }) }
Props can be passed into state, but be explicit that they are only 'initial values'
constructor(props){ var initialNum = this.props.numHearts; this.state = {numHearts: initialNum}; } ... _handleClick(){ this.setState({numHearts: this.state.numHearts + 1}); }
Be sure to make use of componentWillReceiveProps
to sync state and props, if props are passed to state.
componentWillReceiveProps(props){ this.setState({numHearts: props.numHearts}); }
React.createClass({ displayName : '', propTypes: {}, mixins : [], getInitialState : function() {}, componentWillMount : function() {}, componentWillUnmount : function() {}, _onChange : function() {}, _onCreate : function() {}, render : function() {} });
In JSX, anything in {} will be evaluated in JavaScript
{this.state.show && 'This is Shown'} {this.state.on ? ‘On’ : ‘Off’}
For anything more complicated, I have typically been creating a variable inside the render method, suffixed with ‘Html’:
var dinosaurHtml = ''; if (this.state.showDinosaurs) { dinosaurHtml = ( <section> <DinosaurTable /> <DinosaurPager /> </section> ); } return ( <div> ... {dinosaurHtml} ... </div> );
var multilineJsx = ( <header> <Logo /> <Nav /> </header> ); var singleLineJsx = <h1>Simple JSX</h1>;
Components without children should simply close themselves
correct
<Logo />
bad practise
<Logo></Logo>
List iterations are better done inline, especially if each list item will be rendered as a component. You may even be able to reduce to one line with fat arrows.
Note This does require the harmony flag on JSX to be included, which will toggle certain ES6 features (fat arrows, template strings, destructuring, and rest parameters)
render : function() { return ( <ul> {this.state.dinosaursList.map(dinosaur => <DinosaurItem item={dinosaur} />)} </ul> ); }
<form onChange={this.inputHandler}> ... <input type="text" name="newDinosaurName" value={this.state.newDinosaurName} /> ... </form> // input handler function(event) { actions.propagateValue({ field : event.target.name, value : event.target.value }); }
Preferable
<input type="text" value={this.state.newDinosaurName} onChange={this.inputHandler.bind(this, 'newDinosaurName')} />
Alternative
<input type="text" value={this.state.newDinosaurName} onChange={this.inputHandler.bind(this, 'newDinosaurName')} />