React
react入门案例小结
来自于 React理念 的例子
这个案例很简单 所以我就把react流程步骤记一下
步骤
把 UI 划分出组件层级
给你一个UI界面,首先我们得做的就是 看到这个界面 先要想到 应该划分为几个什么样的组件,这关系着你的代码结构,组件的层次关系
这里案例 就可以分为5个组件
导致代码结构(组件包含层次关系)
用 React 创建一个静态版本
完全是:
页面画出来就是上面的效果
要构建一个用于呈现数据模型的静态版本的应用程序,你需要创建能够复用其他组件的组件,并通过 props 来传递数据,无需state 因为state是用来交互的
组件套用组件 最终肯定有一个最大的组件,数据流是单项的,右高到低,单向数据流保证一切模块化并且是快速的
定义 UI 状态的最小(但完整)表示
为了正确构建你的应用,首先你需要考虑你的应用所需要的最小可变状态集;要点是:不要重复
举个简单的例子:例如,如果你正在创建一个 TODO 列表,只要保存一个包含 TODO 事项的数组;不要为计数保留一个单独的状态变量。相反,当你想要渲染 TODO 计数时,只需要使用 TODO 数组的长度就可以了。
这一步还得确定哪些数据是要用state来传送,考虑三个问题
每个数据只要考虑三个问题:
- 它是通过 props 从父级传来的吗?如果是,他可能不是 state。
- 它随着时间推移不变吗?如果是,它可能不是 state。
- 你能够根据组件中任何其他的 state 或 props 把它计算出来吗?如果是,它不是 state。
综上所述 就能知道这个案例的state数据应该是什么了
- 用户输入的搜索文本
- 复选框的值
确定你的 State 应该位于哪里
上面我们就已经确定 哪些数据是要用state传送了,这里要记住的是:
React 中的数据流是单向的,并在组件层次结构中向下传递。
State判别方法:
- 确定每一个需要这个 state 来渲染的组件。
- 找到一个公共所有者组件(一个在层级上高于所有其他需要这个 state 的组件的组件)
- 这个公共所有者组件或另一个层级更高的组件应该拥有这个 state。
- 如果你没有找到可以拥有这个 state 的组件,创建一个仅用来保存状态的组件并把它加入比这个公共所有者组件层级更高的地方。
我们这里可以将state放到最高的组件这FilterProductTable 在构造器里面进行state初始化
添加反向数据流
反向数据流 简单的理解 其实就是回调函数而已
就以输入框为例:
我在输入框中输入数据,触发onChange方法,
onChange方法中又有父级组件传过来的方法,这里数据进行了传递
结果调用父级组件中的handleInputChange方法
此方法又将state进行了赋值,react会执行render方法,会立马渲染UI界面
ProductTable组件中的fileText发生变化,
直接就导致了rows的数据发生了变化 界面当然 效果就发生了改变
就这样一步一步的最终渲染成你要的样子
React 的渲染会非常快 几乎是同时的,简单点还行,如果数据state太多了的话 容易出现思维混乱,这时候就应该沉下心!按上面的步骤一步一步解决
Api教程 之 井字棋
我这边分几次记一下:
首先光光是井字棋 没有其他多余的功能!
只是井字棋
1 首选9个格子都是一个button 实现方法
这样就可以点击有效果触发 并且在格子里面显示数据
State数据 一个是数组 一个是判断条件
Square是记录下你点击的数据,而isXNext是判断是 ‘X’or‘O’
所以我们这边必须 要写一个 winner算法,判断到底是否结束
当我点击的时候 就需要一次次判断 所以得click方法
最后动态改变一下 井字棋旁边的文字
添加记录 方便悔棋
首先 state数据要改
添加 步数 与 历史记录
以一个数组的形式将每一步的squares都保存下来
Render 方法中
添加了 moves这个数据,主要是遍历history,展示出每一步,当我点击其中之一时
修改stepnumber就行,
这也导致了history的改变, 代码:
整体效果
将Board组件的9个格子优化
双层循环解决
一方获胜时,高亮显示连成一线的3颗棋子
首先 修改 winner函数 返回成功的位置
将winnerline当做props传递
Square组件:
最后 在square组件中添加个判断条件即可
显示效果:
来源:http://blog.csdn.net/a153375250/article/details/52667739
state数据
Header头组件
查询框 :
和第一个案例是一样的,staff foreach 筛选一下即可
Select框 :
也是用的foreach 筛选,只是注意 这里需要将其value的1代表主任,在方法里用case switch 转即可
我之前的思路是改变了用filter方法改变了staff,这么做很有问题,当我再次点击的时候就会出错null的错误。
排序:
我这里只写了年龄升与年龄降 拿到state里的staff 用sort排序即可,最后在将排序完的staff setstate一下
Detail,Itempannel组件
删除:
那到state中的staff,用filter方法筛选出与你点击的那一行不同的key,将其排除,返回一个新的staff (我这里的key用的是item.name)
详情:
打开:
在 staff组件render的时候进行判断
将一个界面显示出来
原本的基础界面 css中的style 调低即可
Css代码:
效果:
这里的 两个select框 选中值得代码为
通过此方法 拿到 selSex这个对象
Ref:React支持一个特殊的属性,你可以将这个属性加在任何通过render()返回的组件中。这也就是说对render()返回的组件进行一个标记,可以方便的定位的这个组件实例。这就是ref的作用。
通过 this.refs 来访问这个实例
修改:
首先也是判断条件
拿到修改过后的数据
举个例子:验证(css计时器显现)
之后在 edit方法中 将其一个个赋值
通过key来关联(item里的key 是继承自detaile的key)当然一致
关闭:
设为null 就不显示弹出框了。
Footer组件
添加数据的,其实和上面的修改详情数据类似
注意 push方法返回的不是新数组 而是数组的长度
代码:
================== react_3.html =================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="${base.contextPath}/resources/js/react.js"></script>
<script src="${base.contextPath}/resources/js/react-dom.js"></script>
<script src="${base.contextPath}/resources/js/browser.min.js"></script>
<script src="${base.contextPath}/resources/js/jquery.min.js"></script>
<link href="${base.contextPath}/resources/css/react_3.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="example"></div>
<script type="text/babel">
/*
框架结构
ManageSystem
StaffHeader
StaffItemPanel
StaffItem
StaffItem
StaffItem...
StaffFooter
StaffDetail(只在点击某条目的详情后展示)
*/
class StaffHeader extends React.Component{
constructor(){
super();
this.handlerSearch=this.handlerSearch.bind(this);
this.handlerIdChange=this.handlerIdChange.bind(this);
this.handlerOrderChange=this.handlerOrderChange.bind(this);
}
//search
handlerSearch(e){
this.props.searchallStaff(e.target.value);
}
//筛选
handlerIdChange(e){
this.props.filtStaff(e.target.value);
}
//排序
handlerOrderChange(e){
this.props.sortStaff(e.target.value);
}
render(){
return(
<div>
<h3 style={{'textAlign':'center'}}>人员管理系统</h3>
<table className="opHeader">
<tbody>
<tr>
<td className="headerTd">
<input type="text" placeholder="Search NameOrAge..."
onChange={this.handlerSearch} />
</td>
<td className="headerTd">
<label for='idSelect'>人员筛选</label>
<select id='idSelect' onChange={this.handlerIdChange}>
<option value='0'>全部</option>
<option value='1'>主任</option>
<option value='2'>老师</option>
<option value='3'>学生</option>
<option value='4'>实习</option>
</select>
</td>
<td>
<label for='orderSelect'>排列方式</label>
<select id='orderSelect' onChange={this.handlerOrderChange}>
<option value='0'>不排序</option>
<option value='1'>年龄升</option>
<option value='2'>年龄降</option>
</select>
</td>
</tr>
</tbody>
</table>
</div>
);
}
}
class StaffItem extends React.Component{
constructor(){
super();
this.handlerDelete=this.handlerDelete.bind(this);
this.handlerDetail=this.handlerDetail.bind(this);
}
//delete
handlerDelete(){
//console.log(this.props.item.name);
this.props.removeStaffItem(this.props.item.name);
}
//detail
handlerDetail(){
this.props.detailStaffItem(this.props.item.name);
}
render(){
return(
<tr
style={{'cursor': 'pointer'}}
>
<td className='itemTd'>{this.props.item.name}</td>
<td className='itemTd'>{this.props.item.age}</td>
<td className='itemTd'>{this.props.item.id}</td>
<td className='itemTd'>{this.props.item.sex}</td>
<td className='itemTd'>
<a className="itemBtn" onClick={this.handlerDelete}>删除</a>
<a className="itemBtn" onClick={this.handlerDetail}>详情</a>
</td>
</tr>
);
}
}
class StaffItemPanel extends React.Component{
render(){
let items = [];
//console.log(this.props.filterText);
//console.log(this.props.filtvalue);
this.props.items.forEach((item) => {
if((item.name.indexOf(this.props.filterText)===-1&&(item.age+'').indexOf(this.props.filterText)===-1)
||item.id.indexOf(this.props.filtvalue)==-1)
{
return;
}
items.push(<StaffItem key={item.name} item={item}
removeStaffItem={this.props.removeStaffItem} detailStaffItem={this.props.detailStaffItem}/>);
});
return (
<table className='itemPanel'>
<thead>
<tr>
<th className='itemTd'>姓名<small>(唯一)</small></th>
<th className='itemTd'>年龄</th>
<th className='itemTd'>身份</th>
<th className='itemTd'>性别</th>
<th className='itemTd'>操作</th>
</tr>
</thead>
<tbody>{items}</tbody>
</table>
);
}
}
class StaffFooter extends React.Component{
constructor(){
super();
this.handleAddClick=this.handleAddClick.bind(this);
}
handleAddClick(e){
e.preventDefault();
let item = {};
let addForm = ReactDOM.findDOMNode(this.refs.addForm); //获得指定form
let sex = addForm.querySelector('#staffAddSex');
let id = addForm.querySelector('#staffAddId');
item.name = addForm.querySelector('#staffAddName').value.trim();
item.age = addForm.querySelector('#staffAddAge').value.trim();
item.descrip = addForm.querySelector('#staffAddDescrip').value.trim();
item.sex = sex.options[sex.selectedIndex].value;
item.id = id.options[id.selectedIndex].value;
/*
*表单验证
*/
if(item.name=='' || item.age=='' || item.descrip=='') {
let tips = ReactDOM.findDOMNode(this.refs.tipsUnDone);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
//非负整数
let numReg = /^\d+$/;
if(!numReg.test(item.age) || parseInt(item.age)>150) {
let tips = ReactDOM.findDOMNode(this.refs.tipsUnAge);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
this.props.addStaffItem(item); //回调函数
addForm.reset();
//此处应在返回添加成功信息后确认
let tips = ReactDOM.findDOMNode(this.refs.tips);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
}
render(){
return(
<div>
<h4 style={{'textAlign':'center'}}>人员新增</h4>
<hr/>
<form ref='addForm' className="addForm">
<div>
<label for='staffAddName' style={{'display': 'block'}}>姓名</label>
<input ref='addName' id='staffAddName' type='text' placeholder='Your Name'/>
</div>
<div>
<label for='staffAddAge' style={{'display': 'block'}}>年龄</label>
<input ref='addAge' id='staffAddAge' type='text' placeholder='Your Age(0-150)'/>
</div>
<div>
<label for='staffAddSex' style={{'display': 'block'}}>性别</label>
<select ref='addSex' id='staffAddSex'>
<option value='男'>男</option>
<option value='女'>女</option>
</select>
</div>
<div>
<label for='staffAddId' style={{'display': 'block'}}>身份</label>
<select ref='addId' id='staffAddId'>
<option value='主任'>主任</option>
<option value='老师'>老师</option>
<option value='学生'>学生</option>
<option value='实习'>实习</option>
</select>
</div>
<div>
<label for='staffAddDescrip' style={{'display': 'block'}}>个人描述</label>
<textarea ref='addDescrip' id='staffAddDescrip' type='text'></textarea>
</div>
<p ref="tips" className='tips' >提交成功</p>
<p ref='tipsUnDone' className='tips'>请录入完整的人员信息</p>
<p ref='tipsUnAge' className='tips'>请录入正确的年龄</p>
<div>
<button onClick={this.handleAddClick}>提交</button>
</div>
</form>
</div>
);
}
}
class StaffDetail extends React.Component{
constructor(){
super();
this.handlerClose=this.handlerClose.bind(this);
this.handlerEdit=this.handlerEdit.bind(this);
}
//修改
handlerEdit(){
let item = {};
let editTabel = ReactDOM.findDOMNode(this.refs.editTabel);
let sex = editTabel.querySelector('#staffEditSex');
let id = editTabel.querySelector('#staffEditId');
item.name = editTabel.querySelector('#staffEditName').value.trim();
item.age = editTabel.querySelector('#staffEditAge').value.trim();
item.descrip = editTabel.querySelector('#staffEditDescrip').value.trim();
item.sex = sex.options[sex.selectedIndex].value;
item.id = id.options[id.selectedIndex].value;
item.key = this.props.staffDetail.key;
/*
*表单验证
*/
if(item.name=='' || item.age=='' || item.descrip=='') {
let tips = ReactDOM.findDOMNode(this.refs.DtipsUnDone);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
//非负整数
let numReg = /^\d+$/;
if(!numReg.test(item.age) || parseInt(item.age)>150) {
let tips = ReactDOM.findDOMNode(this.refs.DtipsUnAge);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
return;
}
this.props.editDetail(item);
//此处应在返回修改成功信息后确认
let tips = ReactDOM.findDOMNode(this.refs.Dtips);
tips.style.display = 'block';
setTimeout(function(){
tips.style.display = 'none';
}, 1000);
}
//关闭
handlerClose(){
this.props.closeDetail();
}
componentDidUpdate(){
if(this.props.staffDetail == null){}
else {
let selSex = ReactDOM.findDOMNode(this.refs.selSex);
for(let i=0; i<selSex.options.length; i++){
if(selSex.options[i].value == this.props.staffDetail.sex){
selSex.options[i].selected = 'selected';
break;
}
}
let selId = ReactDOM.findDOMNode(this.refs.selId);
for(let i=0; i<selId.options.length; i++) {
if(selId.options[i].value == this.props.staffDetail.id){
selId.options[i].selected = 'selected';
break;
}
}
}
}
render(){
let staffDetail=this.props.staffDetail;
if(!staffDetail){
return null;
}
return(
<div className="overLay">
<h4 style={{'textAlign':'center'}}>点击'完成'保存修改,点击'关闭'放弃未保存修改并退出.</h4>
<hr/>
<table ref="editTabel">
<tbody>
<tr>
<th>姓名</th>
<td><input id='staffEditName' type="text" defaultValue={staffDetail.name}></input></td>
</tr>
<tr>
<th>年龄</th>
<td><input id='staffEditAge' type="text" defaultValue={staffDetail.age}></input></td>
</tr>
<tr>
<th>性别</th>
<td>
<select ref='selSex' id='staffEditSex'>
<option value="男">男</option>
<option value="女">女</option>
</select>
</td>
</tr>
<tr>
<th>身份</th>
<td>
<select ref="selId" id='staffEditId'>
<option value="主任">主任</option>
<option value="老师">老师</option>
<option value="学生">学生</option>
<option value="实习">实习</option>
</select>
</td>
</tr>
<tr>
<th>个人描述</th>
<td><textarea id='staffEditDescrip' type="text" defaultValue={staffDetail.descrip}></textarea></td>
</tr>
</tbody>
</table>
<p ref='Dtips' className='tips'>修改成功</p>
<p ref='DtipsUnDone' className='tips'>请录入完整的人员信息</p>
<p ref='DtipsUnAge' className='tips'>请录入正确的年龄</p>
<button onClick={this.handlerEdit}>完成</button>
<button onClick={this.handlerClose}>关闭</button>
</div>
);
}
}
//开始组合
var rawData=[{descrip:'我是一匹来自远方的狼。', sex: '男', age: 20, name: '张三', id: '主任',key:0},
{descrip:'我是一匹来自远方的狼。', sex: '女', age: 21, name: '赵静', id: '学生',key:1},
{descrip:'我是一匹来自远方的狼。', sex: '女', age: 22, name: '王二麻', id: '学生',key:2},
{descrip:'我是一匹来自远方的狼。', sex: '女', age: 24, name: '李晓婷', id: '实习',key:3},
{descrip:'我是一匹来自远方的狼。', sex: '男', age: 23, name: '张春田', id: '实习',key:4},
{descrip:'我是一匹来自远方的狼。', sex: '男', age: 22, name: '刘建国', id: '学生',key:5},
{descrip:'我是一匹来自远方的狼。', sex: '男', age: 24, name: '张八', id: '主任',key:6},
{descrip:'我是一匹来自远方的狗。', sex: '男', age: 35, name: '李四', id: '老师',key:7},
{descrip:'我是一匹来自远方的猪。', sex: '男', age: 42, name: '王五', id: '学生',key:8},
{descrip:'我是一匹来自远方的牛。', sex: '男', age: 50, name: '赵六', id: '实习',key:9},
{descrip:'我是一匹来自远方的马。', sex: '男', age: 60, name: '孙七', id: '实习',key:10}];
class App extends React.Component{
constructor(){
super();
this.addStaffItem=this.addStaffItem.bind(this);
this.searchStaff=this.searchStaff.bind(this);
this.filtStaff=this.filtStaff.bind(this);
this.sortStaff=this.sortStaff.bind(this);
this.removeStaffItem=this.removeStaffItem.bind(this);
this.detailStaffItem=this.detailStaffItem.bind(this);
this.closeDetail=this.closeDetail.bind(this);
this.editDetail=this.editDetail.bind(this);
this.state = {
staff : rawData,
filterText:'',
filtvalue:'',
staffDetail: null
};
}
//增
addStaffItem(item){
let staff=this.state.staff;
staff.push(item);
this.setState({
staff: staff
});
}
//删
removeStaffItem(key){
let staff=this.state.staff;
let newStaff = staff.filter(item => {
return item.name != key;
});
this.setState({
staff: newStaff
});
}
//搜索
searchStaff(word) {
this.setState({
filterText: word
});
}
// 筛选
filtStaff(filtType) {
let filtvalue=this.state.filtvalue;
switch(parseInt(filtType)){
case 0:
filtvalue='';
break;
case 1:
filtvalue = '主任';
break;
case 2:
filtvalue = '老师';
break;
case 3:
filtvalue = '学生';
break;
case 4:
filtvalue = '实习';
break;
default: break;
}
this.setState({
filtvalue: filtvalue
})
}
//排序
sortStaff(evm){
let staff=this.state.staff;
switch(parseInt(evm)){
case 0:
break;
case 1: //年龄升
staff.sort(function(item1, item2){
if(item1.age < item2.age)
return -1;
else if (item1.age > item2.age)
return 1;
else
return 0;
});
break;
case 2: //年龄降
staff.sort(function(item1, item2){
if(item1.age < item2.age)
return 1;
else if (item1.age > item2.age)
return -1;
else
return 0;
});
break;
default: break;
}
this.setState({
staff: staff
});
}
//详情
//打开
detailStaffItem(key){
let staff=this.state.staff;
let newStaff = staff.filter(item => {
return item.name == key;
})[0]; //取数组中的第一个
// console.log(Array.isArray(newStaff));
this.setState({
staffDetail: newStaff
});
}
//关闭
closeDetail(){
this.setState({
staffDetail: null
});
}
//修改
editDetail(item){
let staff=this.state.staff;
staff.forEach(staffItem => {
if(staffItem.key == item.key) {
staffItem.name = item.name;
staffItem.sex = item.sex;
staffItem.age = item.age;
staffItem.id = item.id;
staffItem.descrip = item.descrip;
}
});
this.setState({
staff: staff
});
this.closeDetail();
}
render(){
return(
<div>
<StaffHeader searchallStaff={this.searchStaff}
filtStaff={this.filtStaff} sortStaff={this.sortStaff}
/>
<StaffItemPanel items={this.state.staff} filterText={this.state.filterText}
removeStaffItem={this.removeStaffItem} filtvalue={this.state.filtvalue}
detailStaffItem={this.detailStaffItem}/>
<StaffFooter addStaffItem={this.addStaffItem}/>
<StaffDetail staffDetail={this.state.staffDetail} closeDetail={this.closeDetail}
editDetail={this.editDetail}/>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('example')
);
</script>
</body>
</html>
================== react_3.css =================================
body,html {
font-family: '微软雅黑';
}
#app{
width: 600px;
margin: 100px auto;
position: relative;
}
/* header */
.optHeader {
margin: 30px 0;
}
.headerTd {
width: 33%;
text-align: center;
}
.optHeader select {
width: 5em;
}
.itemPanel {
width: 100%;
border: 2px solid #b0c4de;
border-radius: 4px;
margin-bottom: 100px;
}
.itemTd {
width: 20%;
line-height: 1.5em;
text-align: center;
}
.itemBtn {
width: 3em;
font-size: 80%;
color: #1e90ff;
display: inline-block;
}
.tempEmpty {
line-height: 1.5em;
background: #dcdcdc;
}
/* addForm */
.addForm label{
text-align: center;
}
.addForm input, .addForm select, .addForm textarea{
width: 200px;
margin: 0 auto 10px auto;
display: block;
}
.addForm button {
padding: 3px 20px;
background-color: #1e90ff;
color: white;
font-weight: bold;
border-radius: 4px;
margin: 5px auto;
display: block;
}
.tips {
display: none;
color: #708090;
margin: 0 auto;
text-align: center;
}
.overLay {
text-align: center;
z-index: 100;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
padding-top: 50px;
background-color: rgba(255, 255, 255, 0);
animation-name: detailMerge;
animation-duration: 0.4s;
animation-fill-mode: forwards;
}
.overLay table {
width: 100%;
}
.overLay tr {
line-height: 1.5em;
}
.overLay th {
width: 40%;
}
.overLay td {
width: 60%;
text-align: center;
}
.overLay input, .overLay select, .overLay textarea {
width: 200px;
}
.overLay button {
padding: 3px 20px;
background-color: #1e90ff;
color: white;
font-weight: bold;
border-radius: 4px;
margin: 5px auto;
display: inline-block;
}
@keyframes detailMerge {
from {
background-color: rgba(255, 255, 255, 0);
}
to {
background-color: rgba(255, 255, 255, 0.95);
}
}
@-webkit-keyframes detailMerge {
from {
background-color: rgba(255, 255, 255, 0);
}
to {
background-color: rgba(255, 255, 255, 0.8);
}
}