一.前言
你可能不需要Redux,那就别瞎搞!!!
Redux 的适用场景:多交互、多数据源。
二.简介
三个点:action,view,state。
在Redux中 一个 State 对应一个 View。只要 State 相同,View 就相同。而且State 是View导致的。
简单的来说就是用户接触不到State ,只能在View的层面,操作View通过Action发出通知,从而改变State。
当Store 收到 Action 以后,会返回一个新的 State,来改变View。这个过程就叫做 Reducer,且它是一个函数,是一个纯函数
重要:Action只是通知我要改变了,而Reducer才是告诉你怎么去改变,告诉你怎么做才符合社会主义核心价值观,告诉你怎么才无愧于一个当代青年人。
Redux中的state是Redux自己的数据,React通过store.getState来获取,store.dispatch来修改
三.combineReducers
由于整个应用只有一个 State 对象,包含所有数据,对于大型应用来说,这个 State 必然十分庞大,导致 Reducer 函数也十分庞大
-
拆分reducer
将不同的action交给不同的函数来计算,最后合成一个reducerimport { combineReducers } from 'redux'; let defaultState = { count:0, otherdata:0 } const count = (count = defaultState.count,action) => { switch (action.type) { case 'increase': return count + 1 ; default: return count } } const otherdata = (otherdata = defaultState.otherdata,action) => { switch (action.type) { case 'reduce': return otherdata - 1 ; default: return otherdata } } export default combineReducers({ count, otherdata });
四.MiddleWare
实际项目中涉及到很多异步操作,那么在Redux要实现异步,得依靠中间键,中间键如果是非必须的话尽量使用现成的中间键。自己造轮子。。。。。。。。。。你懂得~
一般我们要想添加其他功能选择是在发送Action的时候添加,那就是我们的store.dispatch方法上做文章。
但是store.dispatch作为Action的载体只能接受一个对象,我们添加不了其他功能。
这里就需要用到redux-thunk中间键。
使用方法:命令行在目录下执行:cnpm install --save redux-thunk
import thunk from 'redux-thunk';
使用过后store.dispatch这个方法可以接受一个函数作为参数,ok,可以搞事了!
废话不多说
实践才是检验真理的唯一标准。
这里只是很简略的介绍了Redux,基本的使用方法请参照Redux中文网
五.React-Redux
- 为什么要使用React-Redux
- redux为我们解决了什么?
使用了redux我们在数据传递从下到上的传递不需要一层一层的上报,redux就相当于一个战略指挥中心,你只需要告诉redux,然后让redux去重新计划(渲染) - redux渲染
首先我们得调用store.getState()方法获取到改变后的state,然后再赋值到this.props属性上,然而这只得到了数据。我们知道react的重新渲染需要调用setState方法,而redux则提供了store.subscribe(render)方法,一旦state改变即调用render方法重新渲染 - redux没有解决的问题
使用了redux解决了向上传递的问题,但是向下的传递依然是一层一层的,无法直接通知到某个子组件,解决的方案也有,就是:- 在父组件利用store.getState()获取值
- 在通过原生react的方式一层层往下传递
而react-redux恰好解决了这个问题。
- redux为我们解决了什么?
- React-Redux解决数据下发的问题
- 组件通过在connect时的mapStateToProps函数中映射state值即可直接通过this.props.xxx获取到值
- 组件通过在connect时的mapStateToProps函数中映射相同的state,即可实现不同组件数据共享
在React-Redux中所有组件分为UI组件(presentational component)和容器组件(container component)两大类
UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑
另外React-Redux 提供connect方法,用于从 UI 组件生成容器组件
connect方法有两个参数mapStateToProps和mapDispatchToProps
mapStateToProps:映射Redux state到组件的属性
- mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲
mapDispatchToProps:映射Redux actions到组件的属性
- mapDispatchToProps用来建立 UI 组件的参数到store.dispatch方法的映射
- 它可以是一个函数,也可以是一个对象。如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)
- 定义了 UI 组件的参数怎样发出 Action
需要获取store值的组件或者发送action的组件都需要connect
当容器组件生成过后React-Redux提供了<Provider>组件,让容器组件拿到state。然后在将根组件包起来,这样所有的根组件就能够拿到state了。
像这样:
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
注意:这里App并不能直接获得store中的数据,必须通过connect生成ui组件,才能通过this.props.xxx获取。
那么子组件之间的共享就必须在两个组件connect时的mapStateToProps函数中映射同样的state
简单说一下bindActionCreators
bindActionCreators是redux官方提供的自动合并action并发送dispatch的方法
code:
import * as actions from './actions.js';
function mapDispatchToProps(dispatch){
return {
actions:bindActionCreators(actions,dispatch)
}
}
这样的做法便捷,但是带来的问题是action合并过后,某个组件要发送action时,那么actions就必须要一层一层的往下传,所以不建议使用
五.Do it
使用webpack来构建
package.json
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack --mode production",
"dev": "webpack --watch --mode development"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"css-loader": "^1.0.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack": "^4.15.1",
"webpack-cli": "^3.0.8"
},
"dependencies": {
"react": "^16.8.3",
"react-dom": "^16.8.3",
"react-redux": "^6.0.1",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0"
}
}
项目目录:
这里建议将action和reducer分离开来,因为项目较大的话,你可能会有很多种不同的action或者reducer,方便后期维护。
main.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore , applyMiddleware} from 'redux';
import { Provider, connect } from 'react-redux';
import thunk from 'redux-thunk';
import * as actions from './actions.js';
import reducer from './reducer.js';
import Top from './common/Top.js';
import Content from './common/Content.js'
require('./main.css');
class App extends Component{
render() {
const {onGetDataClick,list} = this.props;
let name = "XXX系统";
return (
<div>
<Top name={name} />
<button onClick={onGetDataClick}>Get</button>
<Content data={list} />
</div>
);
}
}
let store = createStore(reducer,applyMiddleware(thunk)); //使用中间键
function mapStateToProps(state) { //将Redux state映射到组件的属性
return {
list : state.list
}
}
function mapDispatchToProps(dispatch){ //定义事件的action
return{
onGetDataClick:()=>dispatch(actions.getDataAction),
}
}
App = connect(mapStateToProps, mapDispatchToProps)(App) //绑定到App组件
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
actions.js
const getDataAction = (dispatch) => {
$.ajax({
url:"xxxxxxxxxxxxxxxxxxxxxxxx",
type:"post",
dataType:"json",
data:{},
success:function(res){
dispatch({type:"RES_SUCCESS",data:data})
},
error:function(){
dispatch({type:"RES_ERROR"})
},
compelet:function(){
dispatch({type:"RES_COMPELET"})
}
})
}
export {getDataAction}
这里我们dispatch第一次执行咱们的请求,根据不同状态再发送Action来触发 Reducer
reducer.js
const initialState = {
list:[]
}
export default (state = initialState, action) => {
switch (action.type) {
case 'RES_ERROR':
return {
list:"Error"
}
case 'RES_COMPELET':
return {
list:"Compelet"
}
case 'RES_SUCCESS':
return {
list:action.data
}
default:
return initialState;
}
}
Reduce中我们接收到一个Action,然后返回一个State,然后映射到组件的属性上,实现View的更新
Content.js
import React, {Component} from 'react';
export default class extends Component{
render() {
return (
<div className="content">
<table id="managelist">
<thead>
<tr> //此处不可省略tr,不然react会抛出警告
<th>XXX</th>
<th>XXX</th>
<th>XXX</th>
</tr>
</thead>
<tbody>
{this.props.data.map(function(item,index){
return (
...............................
)})}
</tbody>
</table>
</div>
);
}
}
粗略介绍,前路漫漫。