Skip to content

Latest commit

 

History

History
325 lines (263 loc) · 10.8 KB

2. React 简单示例.md

File metadata and controls

325 lines (263 loc) · 10.8 KB

React 简单示例

示例代码放置在 demo/目录下,每个文件夹为一个独立的示例。

先看下这个 demo 最终的样子吧:

demo - 速度与激情

每个示例的入口文件 index.html 结构大体相同:

<!-- React 真实 DOM 将会插入到这里 -->
<div id="demo"></div>

<!-- 引入 React -->
<script src="../../bower_components/react/react.js"></script>
<!-- 引入 JSX 语法格式转换器 -->
<script src="../../bower_components/react/JSXTransformer.js"></script>

<!-- 注意:script 需要注明 type 为 text/jsx 以指定这是一个 JSX 语法格式 -->
<script type="text/jsx" src="demo.js"></script>
</body>

渲染一个虚拟 DOM 为真实 DOM

使用 render() 方法生成真实 DOM 并插入到网页中。

// 使用 React.createClass 创建一个组件
var DemoComponent = React.createClass({
    // 使用 render 方法自动渲染 DOM
    render: function () {
        return (
            <div className="component-hello">
                <h1 className="hello-title">Hello React</h1>
                <p  className="hello-desc">React 初探</p>
                <div className="hello-movies">
                    <p2>我喜欢的电影</p2>
                    <ul>
                        <li className="movie-item">
                            <span className="movie-name">速度与激情7</span>
                            -
                            <span className="movie-date">2015</span>
                        </li>
                    </ul>
                </div>
            </div>
        )
    }
});

// 将组件插入到网页中指定的位置
React.render(<DemoComponent />, document.getElementById('demo'));

在线演示 demo/render

示例代码 demo/render

设置初始数据

第一次渲染真实 DOM 时将使用 getInitialState() 返回的数据。

// 使用 React.createClass 创建一个组件
var DemoComponent = React.createClass({
    // getInitialState 中返回的值将会作为数据的默认值
    getInitialState: function () {
        return {
            title: '我喜欢的电影',
            movies: [
                {
                    id: 7,
                    name: '速度与激情7',
                    date: 2015
                },
                {
                    id: 6,
                    name: '速度与激情6',
                    date: 2013
                }
            ]
        }
    },
    // 使用 render 方法自动渲染 DOM
    render: function () {
        // this.state 用于存储数据
        var title  = this.state.title;
        var movies = this.state.movies.map(function (movie) {
            return (
                <li className="movie-item" key={movie.id}>
                    <span className="movie-name">{movie.name}</span>
                    -
                    <span className="movie-date">{movie.date}</span>
                </li>
            )
        });

        return (
            <div className="component-hello">
                <h1 className="hello-title">Hello React</h1>
                <p  className="hello-desc">React 初探</p>

                <div className="hello-movies">
                    <p2>{title}</p2>
                    <ul>{movies}</ul>
                </div>
            </div>
        )
    }
});

// 将组件插入到网页中指定的位置
React.render(<DemoComponent />, document.getElementById('demo'));

在线演示 demo/get-initial-state

示例代码 demo/get-initial-state

动态更新数据

第二次更新渲染真实 DOM 时将使用 setState() 设置的数据。

// 使用 componentDidMount 在组件初始化后执行一些操作
    componentDidMount: function () {
        // 拉取远程数据
        // 开启假数据服务器:
        // cd fake-server && npm install && node index.js
        this.fetchData();
    },

    // 使用自定义的 fetchData 方法从远程服务器获取数据
    fetchData: function () {
        var self = this;
        // 发起 ajax 获取到数据后调用 setState 方法更新组件的数据
        var url = '../../fake-data/movies.json';
        $.getJSON(url, function (movies) {
            // 本地模拟返回太快了,模拟一下网络延迟
            setTimeout(function() {
                self.setState({
                    movies: movies
                });
            }, 2000);
        });
    },

在线演示 demo/set-state

示例代码 demo/set-state

绑定事件

绑定事件时,我们可以使用 ref="name" 属性对一个 DOM 节点进行标记,同时可以通过 React.findDOMNode(this.refs.name) 获取到这个节点的原生 DOM。

// 使用 render 方法自动渲染 DOM
    render: function () {
        var self = this;
        // this.state 用于存储数据
        var title  = this.state.title;
        var movies = this.state.movies.map(function (movie) {
            return (
                <li className="movie-item" key={movie.id}>
                    <span className="movie-name">{movie.name}</span>
                    -
                    <span className="movie-date">{movie.date}</span>
                    <a href="#" onClick={self.onRemove.bind(null, movie)}>删除</a>
                </li>
            )
        }.bind(this));// 注意这里 bind(this) 修正了上下文

        return (
            <div className="component-hello">
                <h1 className="hello-title">Hello React</h1>
                <p  className="hello-desc">React 初探</p>

                <div className="hello-movies">
                    <p2>{title}</p2>
                    <form onSubmit={this.onAdd}>
                        {/* 注意这里指定 ref 属性,然后我们就可以使用 this.refs.xxx 访问到 */}
                        <input type="text" ref="name" placehlder="输入你喜欢的电影"/>
                        <input type="text" ref="date" placeholder="上映时间"/>
                        <input type="submit" value="提交"/>
                    </form>
                    <ul>{movies}</ul>
                    {this.state.loading ? <div>大家好我是菊花, 我现在在转</div> : null}
                </div>
            </div>
        )
    }
onRemove: function (movie) {
        var id = movie.id;
        console.log(movie)
        // 删除这个 item
        var movies = this.state.movies;
        var len = movies.length;
        var index = -1;
        for(var i = 0; i < len; i++) {
            var _movie = movies[i];
            if (_movie.id === id) {
                index = i;
                break;
            }
        }
        if (index > 0) {
            movies.splice(index, 1);
            this.setState({
                movies: movies
            });
        }
    },

    onAdd: function (e) {
        e.preventDefault();
        var refs = this.refs;
        var refName = React.findDOMNode(refs.name);
        var refDate = React.findDOMNode(refs.date);
        if (refName.value === '') {
            alert('请输入电影名');
            return;
        } else if (refDate === '') {
            alert('请输入上映时间');
            return;
        }
        var movie = {
            // 使用 findDOMNode 获取到原生的 DOM 对象
            name: refName.value,
            date: refDate.value,
            id: Date.now() // 粗暴地以时间数字作为随机 id
        };

        var movies = this.state.movies;
        movies.push(movie);
        this.setState(movies);

        refName.value = '';
        refDate.value = '';
    },

在线演示 demo/events

示例代码 demo/events

多组件与组件嵌套

一个组件就包含了 JSX 模板、数据维护、事件绑定的话,代码量已经够多了,这时候可以采用 AMD/CMD 的方式,将组件进行更细粒度的划分,可以以文件即组件的方式来编写,这里就不上 demo 了。

组件间通信

在 React 中,数据流是单向的,且组件之间可以嵌套,我们可以通过对最顶层组件传递属性方式,向下层组件传送数据。

  • 嵌套组件间,使用 this.props 属性向下传递数据

  • 独立组件之间,自行维护数据则需要自行维护一个全局数据存储,或者使用发布订阅地方式通知数据的更新。

全局数据存储怎么做呢?可以理解为不同的组件获取的数据源一致,在组件的外部维护这个数据集合,或者干脆直接从服务器端获取。

有人会说了,这样很不方便。

但我觉得,既然是一个组件,那就配套有获取组件所需数据的方式,独立组件间有很强的数据依赖时,要么使用上述方式,要么可以简单粗暴,将独立组件用一个顶层组件包裹起来,转化为嵌套组件的关系,即可数据互通。

// 将子组件抽离出来
var LiWrapper = React.createClass({
    render: function () {
        // 使用 this.props 获得传入组件的数据
        var movie = this.props.movie;
        return (
            <li>{/* ... */}</li>
        )
    }
});

// 使用 React.createClass 创建一个组件
var DemoComponent = React.createClass({
    // 使用 getInitialState 的返回值作为数据的默认值
    getInitialState: function () {
      // ...
    },

    // 使用 render 方法自动渲染 DOM
    render: function () {
        // this.state 用于存储数据
        var movies = this.state.movies.map(function (movie) {
            return (
               <LiWrapper movie={movie}/>
            )
        }.bind(this));// 注意这里 bind(this) 修正了上下文

        return (
            <div className="component-hello">
                {/* ... */}
                <div className="hello-movies">
                    <ul>{movies}</ul>
                </div>
            </div>
        )
    }
});

// 将组件插入到网页中指定的位置
// 在使用组件时传入 movies 数据
var movies = [// ...];
React.render(<DemoComponent movies={movies}/>, document.getElementById('demo'));

在线演示 demo/comunications

示例代码 demo/comunications