Tools

Rule

All React components must act like pure functions with respect to their props.

source

components distribution

https://github.com/callemall/material-ui

https://github.com/krasimir/react-place

http://krasimirtsonev.com/blog/article/distributing-react-components-babel-browserify-webpack-uglifyjs

https://github.com/airbnb/javascript/tree/master/react

使用react做的网站

  • https://www.refactor.io/
  • https://www.airbnb.com/s
  • https://www.pinterest.com/
  • https://marvelapp.com/9e8g1gj/screen/31751929

Define React component, the ES5,ES6,ES6+ way

Define component: ES5 vs ES6+

// The ES5 way
var Photo = React.createClass({
  handleDoubleTap: function(e) {  },
  render: function() {  },
});
// The ES6+ way
class Photo extends React.Component {
  handleDoubleTap(e) {  }
  render() {  }
}

Define componentWillMount: ES5 vs ES6+

// The ES5 way
var EmbedModal = React.createClass({
  componentWillMount: function() {  },
});
// The ES6+ way
class EmbedModal extends React.Component {
  constructor(props) {
    super(props);
    // Operations usually carried out in componentWillMount go here
    
  }
}

Define prop types, initilal state and prop defaults: ES5 vs ES6+

// The ES5 way
var Video = React.createClass({
  getDefaultProps: function() {
    return {
      autoPlay: false,
      maxLoops: 10,
    };
  },
  getInitialState: function() {
    return {
      loopsRemaining: this.props.maxLoops,
    };
  },
  propTypes: {
    autoPlay: React.PropTypes.bool.isRequired,
    maxLoops: React.PropTypes.number.isRequired,
    posterFrameSrc: React.PropTypes.string.isRequired,
    videoSrc: React.PropTypes.string.isRequired,
  },
});
// The ES6+ way
class Video extends React.Component {
  static defaultProps = {
    autoPlay: false,
    maxLoops: 10,
  }
  static propTypes = {
    autoPlay: React.PropTypes.bool.isRequired,
    maxLoops: React.PropTypes.number.isRequired,
    posterFrameSrc: React.PropTypes.string.isRequired,
    videoSrc: React.PropTypes.string.isRequired,
  }
  state = {
    loopsRemaining: this.props.maxLoops,
  }
}

Binding this for component’s instance methods: ES5 vs ES6+

// The ES5 way
// Autobinding, brought to you by React.createClass
var PostInfo = React.createClass({
  handleOptionsButtonClick: function(e) {
    // Here, 'this' refers to the component instance.
    this.setState({showOptionsModal: true});
  },
});
// The ES6+ way
// Manually bind, wherever you need to
class PostInfo extends React.Component {
  constructor(props) {
    super(props);
    // Manually bind this method to the component instance...
    this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
  }
  handleOptionsButtonClick(e) {
    // ...to ensure that 'this' refers to the component instance here.
    this.setState({showOptionsModal: true});
  }
}
// Another ES6+ way
// Using Arrow functions and property initializers
class PostInfo extends React.Component {
  handleOptionsButtonClick = (e) => {
    // The body of ES6 arrow functions share the same lexical this as the code that surrounds them
    this.setState({showOptionsModal: true});
  }
  handleOptionsButtonClick2(e) {
    console.log(this); // undefined
  }
}

Another ES6+ way

class PostInfo extends React.Component {
  handleChange(e) {
    console.log(this); // PostInfo
    this.setState({value: event.target.value});
  }
  handleBlur(e) {
    console.log(this); // PostInfo
    this.setState({value: event.target.value});
  }
  render() {
    return (
      <input
        onChange={::this.handleChange}
        onBlur={this.handleBlur.bind(this)} >
    )
  }
}

Counter examples: ES6+ vs ES6 vs ES5

The ES6+ way

ES7+ Property Initializers, inspired by TypeScript’s property initializers.

See more https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#es7-property-initializers, https://babeljs.io/blog/2015/06/07/react-on-es6-plus#property-initializers.

Use babel presets [preset-stage-2.transform-class-properties] to transform.

export class Counter extends React.Component {
  static contextTypes = {
    router: React.PropTypes.object.isRequired
  }
  static propTypes = { initialCount: React.PropTypes.number };
  static defaultProps = { initialCount: 0 };
  state = { count: this.props.initialCount };
  tick() {
    this.setState({ count: this.state.count + 1 });
  }
  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}

The ES6 way

// https://facebook.github.io/react/docs/reusable-components.html#es6-classes
export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
    this.tick = this.tick.bind(this);
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div onClick={this.tick}>
        Clicks: {this.state.count}
      </div>
    );
  }
}
Counter.propTypes = { initialCount: React.PropTypes.number };
Counter.defaultProps = { initialCount: 0 };

The ES5 way

// https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html
var Counter = React.createClass({
  propTypes: {
    initialCount: React.PropTypes.number
  },
  
  getDefaultProps() {
    return {
      initialCount: 0
    };
  },
  
  getInitialState: function() {
    // naming it initialX clearly indicates that the only purpose
    // of the passed down prop is to initialize something internally
    return {count: this.props.initialCount};
  },

  handleClick: function() {
    this.setState({count: this.state.count + 1});
  },

  render: function() {
    return <div onClick={this.handleClick}>{this.state.count}</div>;
  }
});

References

boilerplate

with hot reload

Lifecycle of a component

Lifecycle of a component

React style

两种方式,一种使用内联的style,因为这样可以和React无缝集合,而且不用考虑外部的CSS了,所以好多React组件在使用这个方法。

<Com style={ {width: '50px', height: '100px'} } />

另外一个就是使用className来指定一个selector了,就像传统的方法一样。

<Com className='table' />

Hot reload

Integrating React.js into Existing (jQuery) Web Applications

index.html

<!doctype html>
<html>
  <head>
    <title>Sample App</title>
  </head>
  <body>
    <div id='root'>
    </div>
    <script>
var foo = {
  bar: "test",
  cb: function (p) {
    alert(p);
  }
};
    </script>
    <script src="/static/bundle.js"></script>
  </body>
</html>

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App foo={foo} />, document.getElementById('root'));

App.js

import React, { Component, PropTypes } from 'react';

export default class App extends Component {
  static PropTypes = {
    foo: PropTypes.object.isRequired
  }
  onButtonClick() {
    const foo = this.props.foo;
    foo.cb("aaa");
  }
  render() {
    const foo = this.props.foo;
    return (
      <div>
        <h1>Hello, world.{foo.bar}</h1>
        <button onClick={::this.onButtonClick}>Submit</button>
      </div>
    );
  }
}

React Developer Tools

https://github.com/facebook/react-devtools

Show the source file & line number of react elements

The babel-plugin-transform-react-jsx-source will adds source file and line number to JSX elements.

Show the source file & line number of created react elements, at the bottom of the right panel.

Webpack config

https://github.com/facebookincubator/create-react-app/tree/master/packages/react-scripts/config

react no jsx example

<!doctype html>
<html>
<head>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap-theme.min.css">
<script src="https://npmcdn.com/react@15.5.4/dist/react-with-addons.js"></script>
<script src="https://npmcdn.com/react-dom@15.5.4/dist/react-dom.js"></script>
<script src="https://unpkg.com/react-bootstrap@0.31.0/dist/react-bootstrap.js"></script>
</head>
<body>
<div id="container">
    <!-- This element's contents will be replaced with your component. -->
</div>
<script>
var rootElement =
  React.createElement('div', {}, 
    React.createElement('h1', {}, "Contacts"),
    React.createElement('Button', {}, "Contacts"),
    React.createElement('ul', {},
      React.createElement('li', {},
        React.createElement('h2', {}, "James Nelson"),
        React.createElement('a', {href: 'mailto:james@jamesknelson.com'}, 'james@jamesknelson.com')
      ),
      React.createElement('li', {},
        React.createElement('h2', {}, "Joe Citizen"),
        React.createElement('a', {href: 'mailto:joe@example.com'}, 'joe@example.com')
      )
    )
  )
ReactDOM.render(rootElement, document.getElementById('container'))
</script>
</body>
</html>

Comments

comment out jsx

<div>
  <span>test1</span>
  {/*<span>test2</span>*/}
</div>

comment out props

<input
  id="name"
  // placeholder="test
/>

If you want to test out how some specific JSX is converted into JavaScript, you can try out Babel REPL

the online Babel compiler

Event

  • onClick to register an event handler for the bubbling phase
  • onClickCapture to register an event handler for the capture phase

Application structure

npm packages compare

styles

  • react-with-styles 跟bootstrap样式不好集成

patterns

containers vs components

HTML form element

<form method="POST" action="http://10.3.14.238/fireport/rptfilemanage/download">
  <input type="hidden" name="modid" value={rowObj.id} />
  <input type="hidden" name="modcode" value={rowObj.code} />
  <input type="submit" value="Export" />
</form>

You may also like to know HTML attributes supported by React

i18n

A demo: https://github.com/react-boilerplate/react-boilerplate

See also

References