作者可能是本意想要做一个图书管理系统,不过添加书籍的时候报错,所以简单的页面我们简单的看看

先上github地址:https://github.com/hesisi/react-bookManager

看package.json文件是有用到中间件的,心里多了点期待,也许帮它修复好bug也行呢

先看页面



很简单,可能只是框架的效果吧

看看代码

//src\index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
// 引入中间件
import {Provider} from 'react-redux';
// reducer
import bookReducer from './reducers/bookReducer';
// userreducer
import userReducer from './reducers/userReducer';
import { createStore ,applyMiddleware ,combineReducers } from 'redux';
import thunk from 'redux-thunk'; const rootReducer = combineReducers({
bookReducer,
userReducer
});
//store
const store = createStore(rootReducer,applyMiddleware(thunk)); ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
); store.subscribe(() => {
console.log("================监听store变化:"+JSON.stringify(store));
}); registerServiceWorker();
//app.js中定义了几个去不同页面的路由
import React from 'react';
import ReactDOM from 'react-dom';
import HomeLayout from './layouts/HomeLayout'; import BookAdd from './components/BookAdd';
import BookListContainer from './containers/BookList';
import UserAdd from './components/UserAdd';
import UserListContainer from './components/UserList';
import FileAdd from './components/FileAdd';
import FileList from './components/FileList';
import { BrowserRouter as Router ,Route } from 'react-router-dom';
import registerServiceWorker from './registerServiceWorker'; {/*
store的数据结构 store = {
bookReducer : {
data : [{},{}]
}, userReducer : {
data : [{},{}]
}
} */} class App extends React.Component{
render (){
return (
<Router>
<HomeLayout>
<div>
<Route path="/book/add" component={BookAdd}></Route>
<Route path="/book/list" component={BookListContainer}></Route>
<Route path="/user/add" component={UserAdd} ></Route>
<Route path="/user/list" component={UserListContainer}></Route>
<Route path="/file/add" component={FileAdd}></Route>
<Route path="/file/list" component={FileList}></Route>
</div>
</HomeLayout>
</Router>
); }
} export default App;

接下来我们根据路由看一下页面

//user/list
//应该是数据部分请求没有接口,所以没有数据渲染出来
import React from 'react';
import { Table ,Button ,Popconfirm ,Divider ,Modal} from 'antd';
import {initUserAction} from "../actions/userActions";
import PropTypes from 'prop-types';
import FormLayout from './Form';
import SearchInput from '../components/SearchInput'; class UserList extends React.Component{
constructor(props){
super(props); this.state = {
title : "",
visible : false,
confirmLoading : false,
formData : {},
operation : ""
};
} componentWillMount(){
const {store} = this.context;
fetch("http://localhost:3001/user")
.then(res => res.json())
.then(res => {
store.dispatch(initUserAction(res));
});
} //点击编辑
editHandle(record){
//record:{"id":10002,"name":"PHP从入门到死亡","price":89,"owner_id":10002}
this.setState({
title : "修改",
visible : true,
formData : record,
operation : "edit" //编辑状态
}); } //在子组件中点击添加需要调用的props函数
addHandle(){
this.setState({
title : "添加",
visible : true,
operation : "add"
});
} //Form表单点击确定的时候要执行的props函数,动态获取Input组件的值
comfirmHandle(data){
this.setState({
visible : false,
formData : data
}) let { operation } = this.state;
const { formData } = this.state; if(operation === "edit"){
this.props.editBook(formData);
}else{
this.props.addBook(formData);
} } //取消
cancelHandle(){
this.setState({
visible : false,
formData : {} //点击取消置空record对象
});
} render(){
const { userList, deleteUser } = this.props; //connect传递的props
const { title,visible ,confirmLoading } = this.state;
console.log("===================userlist props:"+JSON.stringify(this.props)); const columns = [{
title : '用户编号',
dataIndex : 'id'
},{
title : '名称',
dataIndex : 'name'
},{
title:'学号',
dataIndex:'student_id'
},{
title:'性别',
dataIndex:'gender'
},{
title:'操作',
render : (text,record) => (
<span type="ghost">
<Button size="small" onClick={() => this.editHandle(record)}>编辑</Button>
<Divider type="vertical" />
<Popconfirm title="确定要删除吗?" onConfirm={() => deleteUser(record)}>
<Button size="small" >删除</Button>
</Popconfirm>
</span>
)
}]; return (
<div>
<div>
<SearchInput addHandle={this.addHandle.bind(this)}/>
</div>
<Table columns={columns} dataSource={userList}/>
<Modal
title={title}
visible= {visible}
confirmLoading = {confirmLoading}
onCancel = {() => this.cancelHandle()}
footer = {null}
>
<FormLayout record={this.state.formData} comfirmHandle={this.comfirmHandle.bind(this)}/>
</Modal>
</div>
);
}
} UserList.contextTypes = {
store: PropTypes.object.isRequired
}; export default UserList;
//src\actions\userActions.js
const INIT_USER_ACTION = "INIT_USER_ACTION";
const ADD_USER_ACTION = "ADD_USER_ACTION";
const DELETE_USER_ACTION = "DELETE_USER_ACTION";
const UPDATE_USER_ACTION = "UPDATE_USER_ACTION"; export const initUserAction = (data) => {
return {
type : INIT_USER_ACTION,
payload : data
}
} export const addUserAction = (data) => {
return {
type : ADD_USER_ACTION,
payload : data
}
} export const deleteUserAction = (id) => {
return {
type : DELETE_USER_ACTION,
payload : id
}
} export const updateUserAction = (data) => {
return {
type : UPDATE_USER_ACTION,
payload : data
}
}
//src\components\SearchInput.js
import React from 'react';
import { Input ,Row ,Col ,Button } from 'antd'; const Search = Input.Search;
class SearchInput extends React.Component{
render(){
return (
//利用栅格系统,使searchInput和button在同一行
<div>
<Row>
<Col span={10}>
<Search
placeholder="输入编号查询..."
onSearch = {value => console.log(value)}
style={{ width:400 ,marginBottom :20}}
enterButton = "搜索"
/>
</Col>
<Col span={3}>
<Button type="primary" onClick={this.props.addHandle}>添加</Button>
</Col>
</Row> </div>
);
}
} export default SearchInput;
//src\components\Form.js
import React from 'react';
import { Form , Input , Button } from 'antd'; const FormItem = Form.Item;
const formItemLayout = {
labelCol : {span : 5},
wrapperCol : {span : 15}
};
class FormLayout extends React.Component{ handleSubmit(e){
e.preventDefault();
const comfirmHandle = this.props.comfirmHandle;
const fieldsValue = this.props.form.getFieldsValue(); //表单校验
this.props.form.validateFields(function(errors,value){
//校验通过
if(!errors){
comfirmHandle(fieldsValue); //获取当前表单数据并当做回调函数的参数传递给父组件
}
}); } render(){
const { getFieldDecorator ,getFeildsValue } = this.props.form;
const { record } = this.props; return (
<Form onSubmit= {this.handleSubmit.bind(this)}>
<FormItem label="编号" {...formItemLayout} style={{display:'none'}}>
{getFieldDecorator('id', {
initialValue : record ? record.id : ""
})(
<Input />
)}
</FormItem>
<FormItem label="名称" {...formItemLayout}>
{getFieldDecorator('name', {
rules: [{
required: true, message: '请输入书籍名称!'
}],
initialValue : record ? record.name : ""
})(
<Input placeholder="请输入书籍名称"/>
)}
</FormItem>
<FormItem label="价格" {...formItemLayout}>
{getFieldDecorator('price', {
rules: [{
required: true, message: '请输入价格!'
},{
pattern : /(^[1-9](\d+)?(\.\d{1,2})?$)|(^(0){1}$)|(^\d\.\d{1,2}?$)/,message:'请输入正确的金额'
}],
initialValue : record ? record.price : ""
})(
<Input placeholder="请输入价格" />
)}
</FormItem>
<FormItem label="借阅者编号" {...formItemLayout}>
{getFieldDecorator('owner_id', {
rules: [{
required: true, message: '请输入借阅者编号!'
},{
pattern : /^(\d{5})$/,message:'请输入5位数字'
}],
initialValue : record ? record.owner_id :""
})(
<Input placeholder="请输入借阅者编号"/>
)}
</FormItem>
<FormItem wrapperCol={{ span: 10, offset: 10 }}>
<Button type="primary" htmlType="submit">
确定
</Button>
</FormItem>
</Form>
);
}
} export default FormLayout = Form.create()(FormLayout);

//src\components\UserAdd.js
import React from 'react'; class UserAdd extends React.Component{
render(){
return (
<div>添加用户</div>
);
}
} export default UserAdd;

//src\components\BookList.js
import React from 'react';
import { Table, Button, Popconfirm, Divider, Modal, message} from 'antd';
import {initBookAction} from "../actions/bookActions";
import PropTypes from 'prop-types';
import FormLayout from './Form';
import SearchInput from '../components/SearchInput'; class BookList extends React.Component{
constructor(props){
super(props); this.state = {
title : "",
visible : false,
confirmLoading : false,
formData : {},
operation : ""
};
} componentWillMount(){
const {store} = this.context;
fetch("http://localhost:3001/book")
.then(res => res.json())
.then(res => {
store.dispatch(initBookAction(res));
});
} //点击编辑
editHandle(record){
//record:{"id":10002,"name":"PHP从入门到死亡","price":89,"owner_id":10002}
this.setState({
title : "修改",
visible : true,
formData : record,
operation : "edit" //编辑状态
});
} //在子组件中点击添加需要调用的props函数
addHandle(){
this.setState({
title : "添加",
visible : true,
operation : "add"
});
} //Form表单点击确定的时候要执行的props函数,动态获取Input组件的值
comfirmHandle(data){ //这个地方要注意setState是异步的,
//只有在重新render的时候state的值才会被重新修改
//所以通过回调函数解决 this.setState({
visible : false,
formData : data
},() => {
let { operation } = this.state;
const { formData } = this.state; if(operation === "edit"){
this.props.editBook(formData);
}else{
this.props.addBook(formData);
//this.props.history.push("/book/list");
} //处理完之后再次置空
this.setState({
formData : {}
}) })
} //取消
cancelHandle(){
this.setState({
visible : false,
formData : {} //点击取消置空record对象
});
} render(){
const { bookList, deleteBook } = this.props; //connect传递的props
const { title,visible ,confirmLoading } = this.state; const columns = [{
title : '图书编号',
dataIndex : 'id',
key : 'id'
},{
title : '名称',
dataIndex : 'name',
key : 'name'
},{
title:'价格',
dataIndex:'price',
key : 'price'
},{
title:'借阅人编号',
dataIndex:'owner_id',
key : 'owner_id'
},{
title:'操作',
key : 'operation',
render : (text,record) => (
<span type="ghost">
<Button size="small" onClick={() => this.editHandle(record)}>编辑</Button>
<Divider type="vertical" />
<Popconfirm title="确定要删除吗?" onConfirm={() => deleteBook(record.id)}>
<Button size="small" >删除</Button>
</Popconfirm>
</span>
)
}]; return (
<div>
<div>
<SearchInput addHandle={this.addHandle.bind(this)}/>
</div>
<Table columns={columns} dataSource={bookList} rowKey="id"/>
<Modal
title={title}
visible= {visible}
confirmLoading = {confirmLoading}
onCancel = {this.cancelHandle.bind(this)}
footer = {null}
destroyOnClose
>
<FormLayout record={this.state.formData} comfirmHandle={this.comfirmHandle.bind(this)}/>
</Modal>
</div>
);
}
} BookList.contextTypes = {
store: PropTypes.object.isRequired,
router:PropTypes.object.isRequired
}; export default BookList;

import React from 'react';
import Form from './Form';
class BookAdd extends React.Component{
render(){
return (
<div>
<Form/>
</div>
);
}
} export default BookAdd;
//src\components\Form.js
import React from 'react';
import { Form , Input , Button } from 'antd'; const FormItem = Form.Item;
const formItemLayout = {
labelCol : {span : 5},
wrapperCol : {span : 15}
};
class FormLayout extends React.Component{ handleSubmit(e){
e.preventDefault();
const comfirmHandle = this.props.comfirmHandle;
const fieldsValue = this.props.form.getFieldsValue(); //表单校验
this.props.form.validateFields(function(errors,value){
//校验通过
if(!errors){
comfirmHandle(fieldsValue); //获取当前表单数据并当做回调函数的参数传递给父组件
}
}); } render(){
const { getFieldDecorator ,getFeildsValue } = this.props.form;
const { record } = this.props; return (
<Form onSubmit= {this.handleSubmit.bind(this)}>
<FormItem label="编号" {...formItemLayout} style={{display:'none'}}>
{getFieldDecorator('id', {
initialValue : record ? record.id : ""
})(
<Input />
)}
</FormItem>
<FormItem label="名称" {...formItemLayout}>
{getFieldDecorator('name', {
rules: [{
required: true, message: '请输入书籍名称!'
}],
initialValue : record ? record.name : ""
})(
<Input placeholder="请输入书籍名称"/>
)}
</FormItem>
<FormItem label="价格" {...formItemLayout}>
{getFieldDecorator('price', {
rules: [{
required: true, message: '请输入价格!'
},{
pattern : /(^[1-9](\d+)?(\.\d{1,2})?$)|(^(0){1}$)|(^\d\.\d{1,2}?$)/,message:'请输入正确的金额'
}],
initialValue : record ? record.price : ""
})(
<Input placeholder="请输入价格" />
)}
</FormItem>
<FormItem label="借阅者编号" {...formItemLayout}>
{getFieldDecorator('owner_id', {
rules: [{
required: true, message: '请输入借阅者编号!'
},{
pattern : /^(\d{5})$/,message:'请输入5位数字'
}],
initialValue : record ? record.owner_id :""
})(
<Input placeholder="请输入借阅者编号"/>
)}
</FormItem>
<FormItem wrapperCol={{ span: 10, offset: 10 }}>
<Button type="primary" htmlType="submit">
确定
</Button>
</FormItem>
</Form>
);
}
} export default FormLayout = Form.create()(FormLayout);
//src\containers\BookList.js
import { connect } from 'react-redux';
import BookList from '../components/BookList';
import { deleteBookAction , addBookAction ,updateBookAction } from '../actions/bookActions';
import { message } from 'antd'; const mapStateToProps = (state) => {
return {
bookList : state.bookReducer.data
};
} const mapDispatchToProps = (dispatch) => {
return {
deleteBook : (id) => {
//dispatch(deleteBookAction(id))
dispatch(dispatch => {
fetch('http://localhost:3001/book/'+id,{
method : 'delete'
})
.then(res => res.json())
.then(res => {
console.log("==============删除返回参数:"+JSON.stringify(res));
dispatch(deleteBookAction(id));
message.success("删除记录成功");
})
.catch(err => {
message.error("删除记录失败");
})
})
},
addBook : (data) => {
//dispatch(addBookAction(data)) dispatch(dispatch => {
fetch('http://localhost:3001/book',{
method : 'post',
body : JSON.stringify({
name : data.name,
price : data.price,
owner_id : data.owner_id
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => res.json)
.then(res => {
console.log("==============添加返回参数:"+JSON.stringify(res));
dispatch(addBookAction(data))
//message.success("添加记录成功");
window.location.reload();
})
.catch(error => {
message.error("添加记录失败")
})
})
},
editBook : (data) => {
//dispatch(updateBookAction(data))
dispatch(dispatch => {
fetch('http://localhost:3001/book/'+data.id,{
method : 'put',
body : JSON.stringify({
name : data.name,
price : data.price,
owner_id : data.owner_id
}),
headers : {
'Content-Type' : 'application/json'
}
})
.then(res => res.json())
.then(res => {
console.log("==============修改返回参数:"+JSON.stringify(res));
dispatch(updateBookAction(data))
message.success("修改记录成功")
})
.catch(error => {
message.error("修改记录失败")
})
})
}
}
}
const BookListContainer = connect(
mapStateToProps,
mapDispatchToProps
)(BookList); export default BookListContainer;
//src\containers\UserList.js
import { connect } from 'react-redux';
import UserList from '../components/UserList';
import { deleteUserAction ,addUserAction ,updateUserAction } from '../actions/userActions'; const mapStateToProps = (state) => {
return {
userList : state.userReducer.data
};
} const mapDispatchToProps = (dispatch) => {
return {
deleteUser : (id) => {
dispatch(deleteUserAction(id))
},
addUser : (data) => {
dispatch(addUserAction(data))
},
editUser : (data) => {
dispatch(updateUserAction(data))
}
}
}
const UserListContainer = connect(
mapStateToProps,
mapDispatchToProps
)(UserList); export default UserListContainer;

【react】react-bookManager的更多相关文章

  1. 【前端】react and redux教程学习实践,浅显易懂的实践学习方法。

    前言 前几天,我在博文[前端]一步一步使用webpack+react+scss脚手架重构项目 中搭建了一个react开发环境.然而在实际的开发过程中,或者是在对源码的理解中,感受到react中用的最多 ...

  2. 【优质】React的学习资源

    React的学习资源 github 地址: https://github.com/LeuisKen/react-collection https://github.com/reactnativecn/ ...

  3. 【温故知新】—— React/Redux/React-router4基础知识&独立团Demo

    前言:React专注View层,一切皆组件:全部使用ES6语法,最新版本为React16. Redux是专注于状态管理的库,和react解耦:单一状态,单向数据流.[独立团github地址] 一.Re ...

  4. 【React】react学习笔记03-React组件对象的三大属性-state

    今天晚上学习了React中state的使用,特做此记录,对于学习的方式,博主仍然推荐直接复制完整代码,对着注释观察现象!: 上文中,我列举了两种React自定义组件的声明,这里我拿方式二进行举例: / ...

  5. 【React】react学习笔记02-面向组件编程

    react学习笔记02-面向组件编程 面向组件编程,直白来说,就是定义组件,使用组件. 以下内容则简单介绍下组建的声明与使用,直接复制demo观测结果即可. 步骤: 1.定义组件   a.轻量组件-函 ...

  6. 【独家】React Native 版本升级指南

    前言 React Native 作为一款跨端框架,有一个最让人头疼的问题,那就是版本更新.尤其是遇到大版本更新,JavaScript.iOS 和 Android 三端的配置构建文件都有非常大的变动,有 ...

  7. 【原】react做tab切换的几种方式

    最近搞一个pc端的活动,搞了一个多月,甚烦,因为相比于pc端,更喜欢移动端多一点.因为移动端又能搞我的react了. 今天主要总结一下react当中tab切换的几种方式,因为tab切换基本上都会用到. ...

  8. 【转载】React入门-Todolist制作学习

    我直接看的这个React TodoList的例子(非常好!): http://www.reqianduan.com/2297.html 文中示例的代码访问路径:http://127.0.0.1:708 ...

  9. 【JAVASCRIPT】React入门学习-文本渲染

    摘要 react 学习包括几个部分: 文本渲染 JSX 语法 组件化思想 数据流 文本渲染 1. 纯文本渲染 <!DOCTYPE html> <html> <head&g ...

  10. 【JAVASCRIPT】React + Redux

    摘要 Redux 数据流图 View 层由React 控制, 根据state 变化 刷新渲染组件,作用是根据更新的数据重新渲染组件 Stroe 层其实就是state存储器,作用是更新数据 Dispat ...

随机推荐

  1. Error:【SLF4J: Class path contains multiple SLF4J bindings.】

    ylbtech-Error:[SLF4J: Class path contains multiple SLF4J bindings.] 1.返回顶部 1. SLF4J: Class path cont ...

  2. 自动安装php7(配置未优化版本)

    #!/bin/bash #by dxd - #only suit for centos/aliyun os, and based on aliyun install script CURR_PATH= ...

  3. PKU--3211 Washing Clothes(01背包)

    题目http://poj.org/problem?id=3211 分析:两个人洗衣服,可以同时洗,但是只能同时洗一种颜色. 要时间最短,那么每一种颜色的清洗时间最短. 转换为,两个人洗同一种颜色的衣服 ...

  4. Referenced assembly does not have a strong name

    Step 1 : Run visual studio command prompt and go to directory where your DLL located. For Example my ...

  5. ElasticSearch入门介绍之会当凌绝顶(一)

    ElasticSearch也是一款非常优秀的开源的全文检索框架,以大名鼎鼎的Apache Lucene为基础,高度封装了更丰富,易用的API,同时与Apache Solr一样,提供了非常强大的分布式集 ...

  6. sql 2000 or sql2005 数据库日志删除

    数据库日志清理(sql 2000 or sql2005)DUMP TRANSACTION crm WITH NO_LOGBACKUP LOG crm WITH NO_LOGDBCC SHRINKDAT ...

  7. [转]WPF 构建无外观(Lookless)控件

    构建一个用户可以使用Template属性设置外观的WPF控件需要以下几步 1.继承自System.Windows.Controls.Control 2.设置DefaultStyleKeyPropert ...

  8. JavaScript RegExp 对象的三种方法

    JavaScript RegExp 对象有 3 个方法:test().exec() 和 compile().(1) test() 方法用来检测一个字符串是否匹配某个正则表达式,如果匹配成功,返回 tr ...

  9. Django项目:CRM(客户关系管理系统)--65--55PerfectCRM实现CRM客户报名状态颜色变化

    # kingadmin.py # ————————04PerfectCRM实现King_admin注册功能———————— from crm import models #print("ki ...

  10. 深入浅出 Java Concurrency (17): 并发容器 part 2 ConcurrentMap (2)[转]

    本来想比较全面和深入的谈谈ConcurrentHashMap的,发现网上有很多对HashMap和ConcurrentHashMap分析的文章,因此本小节尽可能的分析其中的细节,少一点理论的东西,多谈谈 ...