1. 程式人生 > >React在ES6中的用法

React在ES6中的用法

寫在前面

不管是多麼不樂意待見這個不速之客,但ES6已經一點點滲透進了我的生活裡,就連我最愛的React到React Native,預設都把ES6做為首選標準。這是FaceBook有計劃、有聲勢地要把ES6推起來,請問還有誰有狗膽站住來堵著路不走。所以我毅然捧著阮一峰老師的《ES6標準入門》在一旁看了起來……那好,今天就談談如何把React用ES6愉快地跑起來。

入門級Demos

先來幾個簡單的demo體驗一把。注意,例子裡ES5的require請通過browserfiy或webpack來實現,如果你還不會用這兩樣東西,請出門後往前看。

“Hello,XXX”輸出

ES5寫法:

var React = require('react');

var HelloMessage = React.createClass({
    render: function() {
        return (<h1>Hello,{this.props.name}!</h1>);
    }
});

module.exports = React.createClass({
    render: function() {
        return (
            <div>
                <HelloMessage name = "John"
/> </div> ); } });

ES6寫法:

import React,{Component} from 'react';
class HelloMessage extends Component{
  constructor() {
    super();
  }
  render(){
    return <h1>Hello {this.props.name}</h1>;
  }
}
class Output extends Component{
  constructor() {
    super
(); } render(){ return ( <div> <HelloMessage name="John" /> </div> ); } } export default Output;

陣列遍歷顯示

ES5寫法:

var React = require('react');
var RepeatArray = React.createClass({
    render: function() {
        var names = ['Alice', 'Emily', 'Kate'];
        var arrs = [
            <h1>Hello World</h1>,
            <h2>React is awesome</h2>
        ];
        return (
            <div>
            {arr}
            {
                names.map(function (name) {return <div>Hello, {name}!</div>;});     
            }
            </div>
        );
    }
});

module.exports = RepeatArray;

ES6寫法:

import React,{Component} from 'react';
class RepeatArray extends Component{
  constructor() {
    super();
  }
  render(){
    var arr = [
      <h1>Hello world!</h1>,
      <h2>React is awesome</h2>,
    ];
    var names = ['Alice', 'Emily', 'Kate'];
    return (
      <div>
      {arr}
      {
        names.map((name) =>{return <div>Hello, {name}!</div>;} )
      }
      </div>
    );
  }
}
export default RepeatArray;

ol與li的實現

ES5寫法:

var React = require('react');
var RepeatLi = React.createClass({
    render: function() {
        return(
            <ol>
                {
                    this.props.children.map(function(child) {
                        return <li>{child}</li>;
                    });
                }
            </ol> 
        );
    }
});
module.exports = React.createClass({
    render: function() {
        return (
            <div>
                <RepeatLi>
                    <span>hello</span>
                    <span>world</span>
                </RepeatLi>
            </div>
        );
    }
});

ES6寫法:

import React,{Component} from 'react';
class RepeatLi extends Component{
  render(){
    return (
      <ol>
      {
        this.props.children.map((child)=>{return <li>{child}</li>})
      }
      </ol>
    );
  }
}
class RepeatArray extends Component{
  constructor() {
    super();
  }
  render(){
    return (
      <div>

      <RepeatLi>
        <span>hello</span>
        <span>world</span>
      </RepeatLi>

      </div>
    );
  }
}
export default RepeatArray;

Click事件

ES5寫法:

var React = require('react');
var MyComponent = React.createClass({
    handleClick: function() {
        React.findDOMNode(this.refs.myTextInput).focus();
    },
    render: function() {
        return (
            <div>
                <input type="text" ref="myTextInput" />
                <input type="button" value="Focus the text input" onClick= {this.handleClick} />
            </div>
        );
    }
});

module.exports = React.createClass({
    render: function() {
        return (
            <div>
                <MyComponent />
            </div>
        );
    }
});

ES6寫法:

import React,{Component} from 'react';
class FocusText extends Component{
  handleClick(){
    React.findDOMNode(this.refs.myText).focus();
  }
  render(){
    return(
      <div>
        <input type="text" ref="myText" />
        <input type="button" value="focus the text input" onClick={this.handleClick.bind(this)} />
      </div>
    );
  }
}
class RepeatArray extends Component{
  constructor() {
    super();
  }
  render(){
    return (
      <div>
      <FocusText />
      </div>
    );
  }
}
export default RepeatArray;

State的用法,以toggel顯示文字為例

ES5寫法:

var React = require('react');
var LikeButton = React.createClass({
    getInitialState: function() {
        return {liked: false};
    },
    handleClick: function(e) {
        this.setState({liked: !this.state.liked});
    },
    render: function() {
        var text = this.state.liked ? 'like' : 'haven\'t liked';
        return (
            <p onClick = {this.handleClick}> 
                You {text} this.Click to toggle.
            </p>
        );
    }
});

module.exports = React.createClass({
    render: function() {
        return (<div><LikeButton /></div>);
    }
});

ES6寫法:

import React,{Component} from 'react';
class StateUse extends Component{
  constructor(){
    super();
    this.state={
      like:true
    }
  }
  handleClick(){
    this.setState({like:!this.state.like});
  }
  render(){
    var text = this.state.like?'Like':"Unlike";
    return(
      <div>
        <p onClick={this.handleClick.bind(this)}>
        You {text} this.Click the toggle;
        </p>
      </div>
    );
  }
}
class RepeatArray extends Component{
  constructor() {
    super();
  }
  render(){
    return (
      <div>
      <StateUse />
      </div>
    );
  }
}
export default RepeatArray;

onChange事件,以及變數值的同步

ES5寫法:

var React = require('react');
var InputComponent = React.createClass({
    getInitialState: function() {
        return {value: 'Hello!'};
    },
    handleChange: function(e) {
        this.setState({value: e.target.value})
    },
    render: function() {
        var value = this.state.value;
        return (
            <div>
                <input type="text" value={value} onChange={this.handleChangle} />
                <p>{value}</p>
            </div>
        );
    }
});
module.exports = React.createClass({
    render: function() {
        return (<div><InputComponent /></div>)
    }
});

ES6寫法:

import React,{Component} from 'react';
class AsyncText extends Component{
  constructor(){
      super();
      this.state={
        value:'Hello!'
      }
  }
  handleChange(e){
    this.setState({value:e.target.value});
  }
  render(){
    var value= this.state.value;
    return(
      <div>
        <input type="text" value={value} onChange={this.handleChange.bind(this)} />
        <p>
          {value}
        </p>
      </div>
    );
  }
}
class RepeatArray extends Component{
  constructor() {
    super();
  }
  render(){
    return (
      <AsyncText />
      </div>
    );
  }
}
export default RepeatArray;

定時任務事件的嵌入

ES5寫法:

var React = require('react');
var Hello = React.createClass({
    getInitState: function() {
        return {
            opacity: 1.0
        };
    },
    componentDidMount: function() {
        this.timer = setInterval (function() {
            var opacity = this.state.opacity;
            opacity -= .05;
            if (opacity < 0.1) {
                opacity = 1.0;
            }
            this.setState({
                opacity: opacity
            });
        }.bind(this), 100);
    },
    render: function() {
        return (
            <div style={{opacity: this.state.opacity}}>
                Hello {this.props.name}
            </div>
        );
    }
});

module.exports = React.createClass({
    render: function() {
        return (<div><Hello name="world" /></div>);
    }
});

ES6寫法:

import React,{Component} from 'react';
class OpacityWord extends Component{
  constructor(){
    super();
    this.state={
      opacity:1.0
    }
  }
  componentWillMount(){
    let time  =  setInterval(()=>{
      let opacity = this.state.opacity;
      opacity -= 0.5;
      if (opacity<0.1) {
        opacity=1.0;
      }
      this.setState({opacity:opacity});
    }.bind(this),100);
  }
  render(){
    return (
      <div style={{ opacity:this.state.opacity }}>
        Hello, {this.props.name}!
      </div>
    );
  }

}

class RepeatArray extends Component{
  constructor() {
    super();
  }
  render(){
    return (
      <div>
       <OpacityWord />
      </div>
    );
  }
}
export default RepeatArray;

從服務端獲取資料

ES5寫法:

var React = require('react');
var UserGist = React.createClass({
    getInitState: function() {
        return {
            username: '',
            lastGistUrl: ''
        };
    },
    componentDidMount: function() {
        $.get(this.props.source, function(result) {
            var lastGist = result[0];
            this.setState({
                username: lastGist.owner.login,
                lastGistUrl: lastGist.html_url
            });
        }.bind(this));
    },
    render: function() {
        return (
            <div>
                {this.state.username}s last gist is <a href={this.state.lastGistUrl}> here </a>.
            </div>
        );
    }
});

module.exports = React.createClass({
    render: function() {
        return (
            <div>
                <UserGist source="https://api.github.com/users/octocat/gists" />
            </div>
        );
    }
});

ES6寫法:

import React,{Component} from 'react';
class UserGist extends Component{
  constructor(){
    super();
    this.state={
      username:'',
      lastGistUrl:''
    }
  }
  componentWillMount(){
    $.get(this.props.source, function(result) {
      var lastGist = result[0];
      //if (this.isMounted()) {
        this.setState({
          username: lastGist.owner.login,
          lastGistUrl: lastGist.html_url
        });
      //}
    }.bind(this));
  }
  render(){
    return(
      <div>
        {this.state.username} ..
        <a href={this.state.lastGistUrl} >here</a>
      </div>
    );
  }
}
class RepeatArray extends Component{
  constructor() {
    super();
  }
  render(){
    return (
      <div>
      <UserGist source="https://api.github.com/users/octocat/gists" />
      </div>
    );
  }
}
export default RepeatArray;

React Native實戰

寫一個簡單的頁面,支援輸入關鍵字然後以列表形式呈現github裡搜尋出來的結果。效果如下:



ES5寫法:

'use strict';
var React = require('react-native');
var {
    Image,
    ListView,
    TextInput,
    AppRegistry,
    Component,
    StyleSheet,
    Text,
    View,
} = React;

var BASE_URL = "https://api.github.com/search/repositories?q=";

var customView = React.createClass({

    getInitialState: function() {
        return {
            dataSource: new ListView.DataSource({
                rowHasChanged: (row1, row2) => row1 !== row2,
            }),
        };
    },

    render: function() {
        if (this.state.dataSource.getRowCount() === 0) {
            console.log('YES');
        }
        var content = this.state.dataSource.getRowCount() === 0 ?
        <Text style={styles.blankText}>
            Please enter a search term to see results.
        </Text> :
        <ListView
            ref="listview"
            dataSource={this.state.dataSource}
            renderRow={this.renderRow}
            automaticallyAdjustContentInsets={false}
            keyboardDismissMode="onDrag"
            keyboardShouldPersistTaps={true}
            showsVerticalScrollIndicator={false} />;

        return (
            <View style={styles.container}>
                <TextInput
                    autoCapitalize="none"
                    autoCorrect={false}
                    placeholder="Search for a project..."
                    style={styles.searchBarInput} onEndEditing={this.onSearchChange}/>
                {content}
            </View>
        );
    },

    onSearchChange: function(event: Object) {
        var searchTerm = event.nativeEvent.text.toLowerCase();
        var queryURL = BASE_URL + encodeURIComponent(searchTerm);

        fetch(queryURL)
            .then((response) => response.json())
            .then((responseData) => {
                if (responseData.items) {
                    this.setState({
                        dataSource: this.state.dataSource.cloneWithRows(responseData.items),
                    });
                }
            }).done();
    },

    renderRow: function(repo: Object) {
        return (
            <View>
                <View style={styles.row}>
                    <Image source={{uri: repo.owner.avatar_url}}
                    style={styles.profPic}/>
                    <View style={styles.textContainer}>
                        <Text style={styles.title}>
                            {repo.name}
                        </Text>
                        <Text style={styles.subTitle}>
                            {repo.owner.login}
                        </Text>
                    </View>
                    <View style={styles.cellBorder}></View>
                </View>
            </View>
        );
    },
});

var styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#ffffff',
    },
    searchBarInput: {
        marginTop: 30,
        padding: 5,
        fontSize: 15,
        height: 30,
        backgroundColor: '#EAEAEA',
    },
    row: {
        alignItems: 'center',
        backgroundColor: 'white',
        flexDirection: 'row',
        padding: 5,
    },
    cellBorder: {
        backgroundColor: 'rgba(0,0,0,0.1)',
        height: 1,
        marginLeft: 4,
    },
    profPic: {
        width: 50,
        height: 50,
    },
    title: {
        fontSize: 20,
        marginBottom: 8,
        fontWeight: 'bold',
    },
    subTitle: {
        fontSize: 16,
        marginBottom: 8,
    },
    textContainer: {
        paddingLeft: 10,
    },
    blankText: {
        padding: 10,
        fontSize: 20,
    }
});

AppRegistry.registerComponent('customView', () => customView);

ES6寫法:

import React, {
    Image,
    ListView,
    TextInput,
    AppRegistry,
    Component,
    StyleSheet,
    Text,
    View,
}
from 'react-native';
const BASE_URL = "https://api.github.com/search/repositories?q=";

class customView extends Component {

    constructor() {
        super();
    }

    state = {
        dataSource: new ListView.DataSource({
            rowHasChanged: (row1, row2) => row1 !== row2,
        }),
    }

    render() {
        if (this.state.dataSource.getRowCount() === 0) {
            console.log('YES');
        }
        var content = this.state.dataSource.getRowCount() === 0 ?
        <Text style={styles.blankText}>
            Please enter a search term to see results.
        </Text> :
        <ListView
            ref="listview"
            dataSource={this.state.dataSource}
            renderRow={this.renderRow}
            automaticallyAdjustContentInsets={false}
            keyboardDismissMode="onDrag"
            keyboardShouldPersistTaps={true}
            showsVerticalScrollIndicator={false} />;

        return (
            <View style={styles.container}>
                <TextInput
                    autoCapitalize="none"
                    autoCorrect={false}
                    placeholder="Search for a project..."
                    style={styles.searchBarInput} onEndEditing={this.onSearchChange}/>
                {content}
            </View>
        );
    }

    onSearchChange(event: Object) {
        var searchTerm = event.nativeEvent.text.toLowerCase();
        var queryURL = BASE_URL + encodeURIComponent(searchTerm);

        fetch(queryURL)
            .then((response) => response.json())
            .then((responseData) => {
                if (responseData.items) {
                    this.setState({
                        dataSource: this.state.dataSource.cloneWithRows(responseData.items),
                    });
                }
            }).done();
    }

    renderRow(repo: Object) {
        return (
            <View>
                <View style={styles.row}>
                    <Image source={{uri: repo.owner.avatar_url}}
                    style={styles.profPic}/>
                    <View style={styles.textContainer}>
                        <Text style={styles.title}>
                            {repo.name}
                        </Text>
                        <Text style={styles.subTitle}>
                            {repo.owner.login}
                        </Text>
                    </View>
                    <View style={styles.cellBorder}></View>
                </View>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#ffffff',
    },
    searchBarInput: {
        marginTop: 30,
        padding: 5,
        fontSize: 15,
        height: 30,
        backgroundColor: '#EAEAEA',
    },
    row: {
        alignItems: 'center',
        backgroundColor: 'white',
        flexDirection: 'row',
        padding: 5,
    },
    cellBorder: {
        backgroundColor: 'rgba(0,0,0,0.1)',
        height: 1,
        marginLeft: 4,
    },
    profPic: {
        width: 50,
        height: 50,
    },
    title: {
        fontSize: 20,
        marginBottom: 8,
        fontWeight: 'bold',
    },
    subTitle: {
        fontSize: 16,
        marginBottom: 8,
    },
    textContainer: {
        paddingLeft: 10,
    },
    blankText: {
        padding: 10,
        fontSize: 20,
    }
});

AppRegistry.registerComponent('customView', () => customView);