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引入样式)(http://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>
);
}
});
然而,这并不能解决问题,我们要解决的是独立组件之间的通信。
这个问题可能会在下章单独解决。