[TOC]
1.介绍
学习了一段时间的react,学习到不少知识。从开始的js语言,到es6,到es7的async/await,到react,redux,真心长了不少见识。这篇文章主要用来记录下自己的学习成果,和大家分享。
才学习没有多久,因此还存在很多的问题,希望各位大牛多多给出指导意见。
语言比较拙劣,直接看代码的同学可以跳到末尾的github项目地址。
2.使用到的技术栈
-
react
: view框架 -
redux
:状态管理工具,如果是小型程序就没必要用了。为了学习下,demo里面也使用了redux。 -
async/await
:ES7提出来的异步方法,使用很爽。但是要配置babel。- 需要配置
babel-polyfill
- 需要配置
-
webpack
:项目打包工具,将多个jsx和js打包成一个压缩的js,多个css打包成一个压缩的css -
thunk
:为react提供了一种遍历的dispatch方法,这样就能够在异步任务中在任意时刻进行action的触发 -
immutable
:react使用虚拟dom来进行数据的渲染和更新。使用immutable
为js提供了一种不可变变量,让react知道哪些参数是真正变化了的。 -
isomorphic-fetch
:前后端同构的fetch
组件,用来进行网络请求。 -
react-router-dom
:react路由组件,目前是4.2版本。 -
babel-preset-env
:特别提一下这个插件,免去了各种导入present的痛苦,一个插件搞定。
3.项目结构
![](https://img.haomeiwen.com/i4281658/dc04989b0720db2c.png)
4.相关代码
根入口app.jsx
Provider
为整个项目提供统一的store入口。这样在项目中就能够访问到store里面的state了。
import ReactDOM from 'react-dom';
import React, {Component} from 'react';
import {Provider} from 'react-redux';
import store from './Redux/Store/Store';
import MainRouter from './Router/MainRouter';
import './Assets/Styles/Base.css';
import './Assets/Styles/Common.css';
store.subscribe(() => {
});
ReactDOM.render(
<Provider store={store}>
<MainRouter />
</Provider>,
document.getElementById('root')
);
根路由
import React, {Component, PropTypes, createElement} from 'react';
import {Route, Switch,Redirect,BrowserRouter} from 'react-router-dom'
import Layout from '../Layouts/Layout'
const MainRouter = () =>(
<BrowserRouter>
<div>
<Switch>
<Route path='/home' component={Layout} />
<Redirect from="/" to="/home" />
</Switch>
</div>
</BrowserRouter>
);
export default MainRouter;
项目使用常见的BrowserRouter
作为HistoryRouter
Redux
Store
import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
import {todolistReducer} from '../Reducer/TodoListReducer'
import {dialogReducer} from '../Reducer/DialogReducer'
import thunk from 'redux-thunk'
const args = [
combineReducers({
todolistReducer,
dialogReducer
})
];
if (window.__REDUX_DEVTOOLS_EXTENSION__) {
args.push(compose(applyMiddleware(thunk),window.__REDUX_DEVTOOLS_EXTENSION__()));
} else {
args.push(applyMiddleware(thunk));
}
var store = createStore(...args);
export default store;
store
里面引入了两个reducer,使用combineReducers
可以将多个reducer合并到一起。
__REDUX_DEVTOOLS_EXTENSION__
是一个redux的chrome插件,可以在调试的时候查看目前state的情况,非常方便,建议使用。
thunk
是异步dispatch方案,特别适合于网络请求改变state的情况。
Action
import HttpUtil from '../../Util/HttpUtil'
import {HOST} from '../../Util/Constants'
const url=`${HOST}/todolist`;
export const selectTodolistItem=(todolistItem)=>{
return {
type: 'TODOLISTITEM_DID_SELECT',
todolistItem: todolistItem,
}
}
export const getTodolist = ()=> async (dispatch) =>{
let httpUtil = new HttpUtil();
dispatch({
type: 'TODOLIST_WILL_GET'
});
let todolist = await httpUtil.httpGetAsync(url);
dispatch({
type: 'TODOLIST_DID_GET',
todolist:todolist
});
}
上面有两个action,一个action为直接action,返回含有type字段的对象就行。另一个是异步网络请求,这里使用async/await进行异步请求,使用dispatch进行action传递。
Reducer
Reducer是真正操作state的地方。
首先使用一个中间方法来打包reducer
export const createReducer = (initialState, handlers) => {
return (state = initialState, action)=>{
if(handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action)
} else {
return state
}
}
}
然后一个reducer就变成这样了:
import {createReducer} from './CreateReducer'
import {Map,Set, OrderedMap,List} from 'immutable'
const defaultState = Map({
isFetching:false,
todolist:List(),
todolistItem:Map()
});
const todolistWillGet=(state, action) => {
return state.set('isFetching',true);
}
const todolistDidGet=(state, action) => {
state = state.set("todolist", List(action.todolist));
return state.set('isFetching',false);
}
const todolistDidSelect = (state,action)=>{
return state.set('todolistItem', Map(action.todolistItem));
}
export const todolistReducer = createReducer(defaultState, {
'TODOLIST_WILL_GET':todolistWillGet,
'TODOLIST_DID_GET':todolistDidGet,
'TODOLISTITEM_DID_SELECT':todolistDidSelect,
})
createReducer
方法自动将dispatch过来的action的type和reducer中的方法绑定,然后方法中再对state进行操作。
Containers和Components
Containers文件夹主要存放视图框架,并且得到state和action的connect数据。
Components文件夹主要存放一些需要公用的组件。
也有一种分法,就是Containers存放视图组件合connect,然后其余的,包括公用组件和页面的小的逻辑组件都放到Components中去。
import React, {Component} from 'react';
import TodolistTable from './TodolistTable';
import TodolistAdd from './TodolistAdd';
import * as todolistAction from '../../Redux/Action/TodoListAction';
import * as dialogAction from '../../Redux/Action/DialogAction';
import {connect} from 'react-redux';
import './Todolist.css';
class TodolistContainer extends Component{
constructor(props){
super(props)
}
render() {
return (
<div>
<TodolistTable {...this.props}/>
<TodolistAdd {...this.props}/>
</div>
)
}
}
const mapStateToProps = state=> {
const map = state['todolistReducer'];
return {
todolist:map.get('todolist'),
isFetching:map.get('isFetching'),
todolistItem:map.get('todolistItem'),
};
};
const mapDispatchToProps = Object.assign(todolistAction,dialogAction);
export default connect(mapStateToProps, mapDispatchToProps)(TodolistContainer);
extends Component
是继承自react的一个component,TodolistTable
和TodolistAdd
是页面的两个组件,mapStateToProps
是将state通过props的形式传递到这个TodolistContainer
,同理,mapDispatchToProps
是将action通过props的形式传递到TodolistContainer
。
5.项目截图
项目很简陋,基本就这一个页面。想要运行的同学下载下来,然后npm run build
进行webpack打包,然后npm start
,浏览器输入
localhost:8080
就能够运行了。
![](https://img.haomeiwen.com/i4281658/4c630dd38957181c.png)
最后附上github地址,React todo list
网友评论