站点图标 久久日记本

React前后端数据绑定

React前后端数据绑定

目录

*1.上节回顾

*2.使用.NET MVC快速构建后端API

*3.React前端模板

*4.数据绑定

*5.在项目中引入bootstrap

*6.刷新数据

1.上节回顾

将React项目中css样式分离成单独的文件

本节需求:一个简单的电影列表展示。

2.使用.NET MVC快速构建后端API

如何使用.NET MVC创建后端API,在这里不再详细讨论。

新建MVC4项目RenderData

model.cs

public class Movies
{
  public Int64 Id { get; set; }
  public string Name { get; set; }
  public string Logo { get; set; }
  public Int64 UpdateId { get; set; }
  public DateTime UpdateDate { get; set; }
}

MovieController.cs

[HttpGet]
public ActionResult GetList()
{
  StreamReader sr = new StreamReader(Server.MapPath("/data/movielist.json"));
  return Content(sr.ReadToEnd());
}

[HttpGet]
public ActionResult OneMovie()
{
  StreamReader sr = new StreamReader(Server.MapPath("/data/onemovie.json"));
  return Content(sr.ReadToEnd());
}

movielist.json

{
  "success": true,
  "data": [
    {
      "id": 1,
      "name": "A",
      "logo": "../Img/1.jpg",
      "updateid": 0,
      "updatedate": "2016-10-26 00:00:00"
    },
    {
      "id": 2,
      "name": "B",
      "logo": "../Img/1.jpg",
      "updateid": 0,
      "updatedate": "2016-10-27 00:00:00"
    },
    {
      "id": 3,
      "name": "C",
      "logo": "../Img/1.jpg",
      "updateid": 0,
      "updatedate": "2016-10-28 00:00:00"
    },
    {
      "id": 4,
      "name": "D",
      "logo": "../Img/1.jpg",
      "updateid": 0,
      "updatedate": "2016-10-29 00:00:00"
    },
    {
      "id": 5,
      "name": "E",
      "logo": "../Img/1.jpg",
      "updateid": 0,
      "updatedate": "2016-10-30 00:00:00"
    },
    {
      "id": 6,
      "name": "F",
      "logo": "../Img/1.jpg",
      "updateid": 0,
      "updatedate": "2016-11-01 00:00:00"
    },
    {
      "id": 7,
      "name": "I",
      "logo": "../Img/1.jpg",
      "updateid": 0,
      "updatedate": "2016-11-02 00:00:00"
    }
  ]
}

OneMovie.json

{
  "success": true,
  "data": {
    "id": 1,
    "name": "A",
    "logo": "../Img/1.jpg",
    "updateid": 0,
    "updatedate": "2016-10-26 00:00:00"
  }
}

重新编译=>选中 Controllers =>右键 在浏览器中浏览:

我们得到2个api:

获取电影列表
http://localhost:43286/movie/getlist

获取一个电影的详细信息
http://localhost:43286/movie/onemovie

注意,需要给web.config配置上允许访问的标识,在这里我们允许任何前端直接请求api,以免出现跨域的报错。

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Methods" value="OPTIONS,POST,GET"/>
        <add name="Access-Control-Allow-Headers" value="x-requested-with"/>
        <add name="Access-Control-Allow-Origin" value="*" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
3.React前端模板

创建一个前端项目FrontEnd

文件夹结构如下:

project--
|
|--components
|       |
|       |--icon.jsx
|       |--movie.jsx
|       |--index.jsx
|
|--css
|   |--movie.css
|    
|--index.html
|--package.json
|--webpack.config.js

结合上节,我们制作一个简单的html静态页面:

module.html

<html>

<head>
    <title></title>
    <style>
        .movie {
            width: 200px;
            height: 80px;
        }

        .left {
            width: 100px;
            height: 80px;
            float: left;
        }

        .right {
            width: 100px;
            height: 80px;
            float: left;
        }

        .logo {
            width: 100%;
            height: 100%;
        }

        .logo span {
            width: 100px;
            text-align: center;
            margin-top: -15px;
            background-color: silver;
            position: absolute;
            font-size: 10pt;
            color: white;
            opacity: 0.8;
        }
    .intro{
      width: 100px;
      word-wrap: break-word;
      /* word-spacing: inherit; */
      /* display: block; */
      /* white-space: nowrap; */
      /* overflow: hidden; */
      /* text-overflow: ellipsis; */
    }
    </style>
</head>
<body>
  <div class="movie">
    <div class="left">
      <div class="logo">
        <img src="img/1.jpg" width="100" height="70" />
        <span>2012-01-01</span>
      </div>
    </div>
    <div class="right">
      <div class="name">aaaaa</div>
      <div class="intro">introintrointrointrointrointro</div>
    </div>
  </div>
</body>
</html>

效果图:

参考上节知识,分离样式,css和组件jsx,这里直接上代码

icon.jsx

'use strict'
var React = require('react');
var DateFormat=require('../common/dateformat')

module.exports = React.createClass({
  displayName: 'Icon',
  render: function () {
    return (
      <div className="logo">
        <img src="" width="100" height="70" />
        <span>2012-01-01</span>
      </div>
    );
  }
});

movie.jsx

'use strict'
var React = require('react');
var Icon = require('./icon');
module.exports = React.createClass({
  displayName: 'Movie',
  render: function () {
    var movieshtml = this.state.movies.map(function (movie) {
      // 将movie传入
      return (
        <div className="movie" key={movie.id}>
          <div className="left">
            <Icon data={movie}></Icon>
          </div>
          <div className="right">
            <div className="name">{movie.name}</div>
            <div className="intro">{movie.updatedate}</div>
          </div>
        </div >
      )
    });
    return <div>{movieshtml}</div>
  }
});

index.jsx

'use strict'
var ReactDOM = require('react-dom');
var Icon = require('./icon');
var Movie = require('./movie');
ReactDOM.render(
  <div>
    <Movie></Movie>
  </div>,
  document.getElementById('content')
);

movie.jsx中,

api返回的是一段json对象数组,然而我们需要在render中渲染出html列表返回到index.jsx

在最初的index.jsx中,

this.state.movies.map(function (movie) {
  com = com + '<div className="movie" key={movie.id}>\
    <div className="left">\
      <Icon data={movie}></Icon>\
    </div>\
    <div className="right">\
      <div className="name">{movie.name}</div>\
      <div className="intro">{movie.updatedate}</div>\
    </div>\
  </div>';
})
return (
  { com }
)

使用html拼接,然而并不奏效,
console报错invariant.js:38 Uncaught Error: Movie.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.(…)

4.数据绑定

在修改中发现,map执行的遍历会在结果返回,将上面代码更新为:

movie.jsx

  var movieshtml = this.state.movies.map(function (movie) {
    // 将movie传入
    return (
      <div className="movie" key={movie.id}>
        <div className="left">
          <Icon data={movie}></Icon>
        </div>
        <div className="right">
          <div className="name">{movie.name}</div>
          <div className="intro">{movie.updatedate}</div>
        </div>
      </div >
    )
  });
  return <div>{movieshtml}</div>

由于icon.jsx保存了电影的信息,所以将{movie}传入Icon,放到data属性中。

<Icon data={movie}></Icon>

以便在icon.jsx中使用。

icon.jsx中,我们需要绑定上电影信息(如果你用过angularjs1,这里的绑定会清晰,这里不再探讨为什么这么绑定,你可以参考我的angularjs的文章),
修改一下上面icon.jsx

  render: function () {
    var movie = this.props.data;
    var date = new Date(movie.updatedate);
    return (
      <div className="logo">
        <img src={movie.logo} width="100" height="70" />
        <span>{movie.updatedate,'yyyy-MM-dd'}</span>
      </div>
    );
  }

然而在module.html中,图片上的时间格式为yyyy-MM-dd,而目前api返回的时间格式为'yyyy-MM-dd HH:mm:ss',
假设已经存在一个时间转换方法DateFormat

所以上面return回的代码可以更新为

    return (
      <div className="logo">
        <img src={movie.logo} width="100" height="70" />
        <span>{DateFormat(movie.updatedate,'yyyy-MM-dd')}</span>
      </div>
    );

新建common/dateformat.jsx,我们将string字符串时间转换为yyyy-MM-dd格式:

dateformat.jsx

module.exports = function (date, format) {
  date = new Date(date);
  var year = date.getFullYear();
  var month = date.getMonth() + 1;
  var day = date.getDate();
  var hour = date.getHours();
  var minute = date.getMinutes();
  var second = date.getSeconds();
  var millisecond = date.getMilliseconds();
  // yyyy-MM-dd HH:mm:ss
  var str = '';
  // yyyy  yy
  if (new RegExp('y{4}').test(format) && new RegExp('y{2}').test(format)) {
    str = str + year.toString();
  }
  if (!new RegExp('y{4}').test(format) && new RegExp('y{2}').test(format)) {
    str = str + year.toString().substr(2, 3);
  }
  if (new RegExp('M{2}').test(format)) {
    str = str + '-' + (month < 10 ? ('0' + month.toString()) : month.toString());
  }
  if (new RegExp('d{2}').test(format)) {
    str = str + '-' + (day < 10 ? ('0' + day.toString()) : day.toString());
  }
  if (new RegExp('H{2}').test(format)) {
    str = str + ' ' + (hour < 10 ? ('0' + hour.toString()) : hour.toString());
  }
  if (new RegExp('m{2}').test(format)) {
    str = str + ':' + (minute < 10 ? ('0' + minute.toString()) : minute.toString());
  }
  if (new RegExp('s{2}').test(format)) {
    str = str + ':' + (second < 10 ? ('0' + second.toString()) : second.toString());
  }
  return str;
}

绑定完毕,我们在渲染的时候初始化一段数据,
在AngularJS1中,状态通常保存在$scope中,
而在react,状态通常保存在state中。

阮一峰在React 入门实例教程将到组件的生命周期包括三个状态:

组件的生命周期分成三个状态:
• Mounting:已插入真实 DOM
• Updating:正在被重新渲染
• Unmounting:已移出真实 DOM
React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。
• componentWillMount()
• componentDidMount()
• componentWillUpdate(object nextProps, object nextState)
• componentDidUpdate(object prevProps, object prevState)
• componentWillUnmount()
此外,React 还提供两种特殊状态的处理函数。
• componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
• shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用

给movie.jsx初始化状态,添加以下代码:

  getInitialState: function () {
    return {
      movies: [{
        id: 1,
        name: "A",
        logo: "../Img/1.jpg",
        updateid: 0,
        updatedate: "2016-10-26 00:00:00"
      }
      ]
    };
  },
  componentDidMount: function () {
  },

cd到前端代码根目录,CMD运行 > npm start

打开 http://localhost:8889/
一个图文简介电影介绍已经被显示的浏览器上。

效果图:

而我们要获得一个列表,在componentDidMount方法中添加ajax请求数据,在页面dom被渲染之后isMounted更新数据:

  componentDidMount: function () {
    $.get("http://localhost:43286/Movie/getlist", function (result) {
      var movies = JSON.parse(result).data;
      if (this.isMounted()) {
        this.setState({
          movies: movies
        });
        // console.log(this);
      }
    }.bind(this)); //需要绑定一下this,why?
  }

刷新页面,数据已经更新。

效果图:

*5.在项目中引入bootstrap

修改配置文件,这里可以参考第三节(上一节):(React引入样式)(https://blog.99diary.com/2016/10/28/react引入样式/) 引入bootstrap样式Panels

修改movie.jsx

  render: function () {
    var movieshtml = this.state.movies.map(function (movie) {
      // 将movie传入
      return (
        // 在这里key需要放在list top-level标签上,否则报错
        // react-with-addons.js:22809Warning: Each child in an array or iterator should have a unique "key" prop. Check the top-level render call using <div>. See https://fb.me/react-warning-keys for more information.
        <div className="panel panel-primary" key={movie.id}>
          <div className="movie panel-body">
            <div className="left">
              <Icon data={movie}></Icon>
            </div>
            <div className="right">
              <div className="name">{movie.name}</div>
              <div className="intro">{movie.updatedate}</div>
            </div>
          </div >
        </div>
      )
    });
    return <div>{movieshtml}</div>
  }

效果图:

可见bootstrap已经被应用成功。

注:这里不考虑美观。

6.刷新数据

这里可以参考第二节:react中简单的数据绑定与事件

在项目中新建一个组件:刷新按钮,

refreshbutton.jsx

'use strict'
var React = require('react');
module.exports = React.createClass({
  displayName: 'RefreshButton',
  refresh: function () {
    console.log(this);
    $.get("http://localhost:43286/Movie/getnewlist", function (result) {
      var movies = JSON.parse(result).data;
      this.setState({
        movies: movies
      });
      console.log(this);
      // if (this.isMounted()) {
      // }
    }.bind(this));
  },
  render: function () {
    // react-with-addons.js:22809Warning: Each child in an array or iterator should have a unique "key" prop. Check the top-level render call using <div>. 
    // See https://fb.me/react-warning-keys for more information.
    return (
      <button type="button" key="refreshbutton" className="btn btn-xs btn-info" onClick={this.refresh}>Refresh</button>
    );
  }
});

然而,这并不能解决问题,我们要解决的是独立组件之间的通信。

这个问题可能会在下章单独解决。

本节源码地址(import data)

退出移动版