学习目标
在准备1中,我们利用Bootstrap绘制了一个静态的计算器页面,那个页面没有任何功能。在准备2中,我们将利用React把计算器功能实现。
准备2中不涉及npm相关,准备3中会介绍并使用
学习参考
React版本:15.3.2
可先按以下链接顺序简要学习,重点在跑其中的例子和理解React的思想:
JavaScript相关的内容,可以参考如下链接:
以上链接均来自 菜鸟教程
以下链接内容很值得后续学习:
实践示例
准备工作
- 下载bootstrap-3.3.7-dist.zip并解压,提取下面目录结构需要的文件
- 下载react-15.3.2.zip并解压,提取下面目录结构需要的文件
- 建立如下方所示的目录结构
目录结构
-prepare2
-js
jquery.min.js
bootstrap.min.js
react.js
react-dom.js
browser.min.js
-css
bootstrap.min.css
index.html
引入依赖
修改index.html的head部分:
<head>
<title>计算器</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/bootstrap.min.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/react.js"></script>
<script type="text/javascript" src="js/react-dom.js"></script>
<script type="text/javascript" src="js/browser.min.js"></script>
</head>
组件划分
我们将组件按如下思路划分:
- 整个网页应用有一个根组件,App组件
- 计算结果显示是一个组件,ResultShow组件
- 按钮组应该是一个组件,ButtonContainer组件
- 按钮也应该是一个组件,Button组件
组件的树状结构应该是这样的:
-App
ResultShow
-ButtonContainer
Button
......
Button
确定状态
先来思考一下这个计算器是如何工作的的:
- 计算器能存四个值,一个是运算结果,一个是输入数字,一个是加法标记,一个是运算结束标记
- 计算器初始运算结果、输入数字为0,加法标记初始为不存在,运算结束标记初始为存在
- 计算器显示规则:对运算结果操作则显示运算结果,对输入数字操作则显示输入数字,都操作则显示运算结果
- 计算器工作状态如下:
-数字按钮
-有加法标记
数字追加到输入数字
-无加法标记
-有运算结束标记
数字赋值到运算结果
清除运算结束标记
-无运算结束标记
数字追加到运算结果
-加法按钮
-有加法标记
输入数字+运算结果->运算结果
输入数字清零
-无加法标记
设置加法标记
-等于按钮
-有加法标记
输入数字+运算结果->运算结果
输入数字清零
清除加法标记
设置运算结束标记
-无加法标记
设置运算结束标记
这个计算器网页应用的状态应该有如下几个:
- 显示数字应该是一个状态
- 运算结果应该是一个状态
- 输入数字应该是一个状态
- 加法标记应该是一个状态
- 运算结束应该是一个状态
在这里,5个状态都可以存储在App组件中
组件实现
ResultShow组件:
var ResultShow = React.createClass({
propTypes: {
showNumber: React.PropTypes.number.isRequired
},
render: function () {
return (
<div className="row clearfix">
<div className="col-xs-4 column col-xs-offset-8">
<center><h3>{this.props.showNumber}</h3></center>
</div>
</div>
);
}
});
Button组件:
var Button = React.createClass({
propTypes: {
buttonType: React.PropTypes.string.isRequired,
handleOnClick: React.PropTypes.func.isRequired
},
render: function () {
return (
<div className="row clearfix">
<div className="col-xs-12 column">
<center><button type="button" className="btn btn-success" onClick={this.props.handleOnClick}>{this.props.buttonType}</button></center>
</div>
</div>
);
}
});
ButtonContainer组件:
var ButtonContainer = React.createClass({
propTypes: {
handleOnClick: React.PropTypes.func.isRequired
},
render: function () {
var item1 = [];
var item2 = [];
var item3 = [];
for (var i=1; i<=3; i++) {
item1.push(<Button key={3*i-2+""} handleOnClick={this.props.handleOnClick.bind(null, 3*i-2+"")} buttonType={3*i-2+""} />);
item2.push(<Button key={3*i-1+""} handleOnClick={this.props.handleOnClick.bind(null, 3*i-1+"")} buttonType={3*i-1+""} />);
item3.push(<Button key={3*i-0+""} handleOnClick={this.props.handleOnClick.bind(null, 3*i-0+"")} buttonType={3*i-0+""} />);
}
item1.push(<Button key={"0"} handleOnClick={this.props.handleOnClick.bind(null, "0")} buttonType={"0"} />);
item2.push(<Button key={"+"} handleOnClick={this.props.handleOnClick.bind(null, "+")} buttonType={"+"} />);
item3.push(<Button key={"="} handleOnClick={this.props.handleOnClick.bind(null, "=")} buttonType={"="} />);
return (
<div className="row clearfix">
<div className="col-xs-4 column">
{item1}
</div>
<div className="col-xs-4 column">
{item2}
</div>
<div className="col-xs-4 column">
{item3}
</div>
</div>
);
}
});
App组件:
var App = React.createClass({
getInitialState: function () {
return {
showNumber: 0,
resultNumber : 0,
inputNumber: 0,
addFlag: false,
endFlag: true
};
},
handleOnClick: function(buttonType, event) {
switch (buttonType) {
case '+' :
if (this.state.addFlag) {
this.setState({
resultNumber: this.state.resultNumber + this.state.inputNumber,
inputNumber: 0,
showNumber: this.state.resultNumber + this.state.inputNumber //new resultNumber
});
} else {
this.setState({
addFlag: true
});
}
break;
case '=' :
if (this.state.addFlag) {
this.setState({
resultNumber: this.state.resultNumber + this.state.inputNumber,
inputNumber: 0,
addFlag: false,
endFlag: true,
showNumber: this.state.resultNumber + this.state.inputNumber //new resultNumber
});
} else {
this.setState({
endFlag: true
});
}
break;
default:
if (this.state.addFlag) {
this.setState({
inputNumber: this.state.inputNumber*10 + parseInt(buttonType),
showNumber: this.state.inputNumber*10 + parseInt(buttonType) //new inputNumber
});
} else {
if (this.state.endFlag) {
this.setState({
resultNumber: parseInt(buttonType),
endFlag: false,
showNumber: parseInt(buttonType) //new resultNumber
});
} else {
this.setState({
resultNumber: this.state.resultNumber*10 + parseInt(buttonType),
showNumber: this.state.resultNumber*10 + parseInt(buttonType) //new resultNumber
});
}
}
break;
}
},
render: function () {
return (
<div className="container">
<div className="row clearfix">
<div className="col-xs-12 column">
<div className="row clearfix">
<div className="col-xs-6 column col-xs-offset-3">
<div className="jumbotron">
<div className="pull-left">
<h1><strong>计算器</strong></h1>
</div>
<p></p>
<div className="row clearfix">
<div className="col-xs-12 column">
<ResultShow showNumber={this.state.showNumber} />
<p></p>
<ButtonContainer handleOnClick={this.handleOnClick} />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
});
全部代码
index.html的全部代码,配合上文的目录结构,本地直接用浏览器打开就可以看到效果:
<!DOCTYPE html>
<html>
<head>
<title>计算器</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/bootstrap.min.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/react.js"></script>
<script type="text/javascript" src="js/react-dom.js"></script>
<script type="text/javascript" src="js/browser.min.js"></script>
</head>
<body>
<div id="app">
</div>
<script type="text/babel">
var ResultShow = React.createClass({
propTypes: {
showNumber: React.PropTypes.number.isRequired
},
render: function () {
return (
<div className="row clearfix">
<div className="col-xs-4 column col-xs-offset-8">
<center><h3>{this.props.showNumber}</h3></center>
</div>
</div>
);
}
});
var Button = React.createClass({
propTypes: {
buttonType: React.PropTypes.string.isRequired,
handleOnClick: React.PropTypes.func.isRequired
},
render: function () {
return (
<div className="row clearfix">
<div className="col-xs-12 column">
<center><button type="button" className="btn btn-success" onClick={this.props.handleOnClick}>{this.props.buttonType}</button></center>
</div>
</div>
);
}
});
var ButtonContainer = React.createClass({
propTypes: {
handleOnClick: React.PropTypes.func.isRequired
},
render: function () {
var item1 = [];
var item2 = [];
var item3 = [];
for (var i=1; i<=3; i++) {
item1.push(<Button key={3*i-2+""} handleOnClick={this.props.handleOnClick.bind(null, 3*i-2+"")} buttonType={3*i-2+""} />);
item2.push(<Button key={3*i-1+""} handleOnClick={this.props.handleOnClick.bind(null, 3*i-1+"")} buttonType={3*i-1+""} />);
item3.push(<Button key={3*i-0+""} handleOnClick={this.props.handleOnClick.bind(null, 3*i-0+"")} buttonType={3*i-0+""} />);
}
item1.push(<Button key={"0"} handleOnClick={this.props.handleOnClick.bind(null, "0")} buttonType={"0"} />);
item2.push(<Button key={"+"} handleOnClick={this.props.handleOnClick.bind(null, "+")} buttonType={"+"} />);
item3.push(<Button key={"="} handleOnClick={this.props.handleOnClick.bind(null, "=")} buttonType={"="} />);
return (
<div className="row clearfix">
<div className="col-xs-4 column">
{item1}
</div>
<div className="col-xs-4 column">
{item2}
</div>
<div className="col-xs-4 column">
{item3}
</div>
</div>
);
}
});
var App = React.createClass({
getInitialState: function () {
return {
showNumber: 0,
resultNumber : 0,
inputNumber: 0,
addFlag: false,
endFlag: true
};
},
handleOnClick: function(buttonType, event) {
switch (buttonType) {
case '+' :
if (this.state.addFlag) {
this.setState({
resultNumber: this.state.resultNumber + this.state.inputNumber,
inputNumber: 0,
showNumber: this.state.resultNumber + this.state.inputNumber //new resultNumber
});
} else {
this.setState({
addFlag: true
});
}
break;
case '=' :
if (this.state.addFlag) {
this.setState({
resultNumber: this.state.resultNumber + this.state.inputNumber,
inputNumber: 0,
addFlag: false,
endFlag: true,
showNumber: this.state.resultNumber + this.state.inputNumber //new resultNumber
});
} else {
this.setState({
endFlag: true
});
}
break;
default:
if (this.state.addFlag) {
this.setState({
inputNumber: this.state.inputNumber*10 + parseInt(buttonType),
showNumber: this.state.inputNumber*10 + parseInt(buttonType) //new inputNumber
});
} else {
if (this.state.endFlag) {
this.setState({
resultNumber: parseInt(buttonType),
endFlag: false,
showNumber: parseInt(buttonType) //new resultNumber
});
} else {
this.setState({
resultNumber: this.state.resultNumber*10 + parseInt(buttonType),
showNumber: this.state.resultNumber*10 + parseInt(buttonType) //new resultNumber
});
}
}
break;
}
},
render: function () {
return (
<div className="container">
<div className="row clearfix">
<div className="col-xs-12 column">
<div className="row clearfix">
<div className="col-xs-6 column col-xs-offset-3">
<div className="jumbotron">
<div className="pull-left">
<h1><strong>计算器</strong></h1>
</div>
<p></p>
<div className="row clearfix">
<div className="col-xs-12 column">
<ResultShow showNumber={this.state.showNumber} />
<p></p>
<ButtonContainer handleOnClick={this.handleOnClick} />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
});
ReactDOM.render(
<App />,
document.getElementById('app')
);
</script>
</body>
</html>