redux 入门介绍
从这节起我们开始学习 Redux,一种新型的前端“架构模式”。经常和 React.js 一并提出,你要用 React.js 基本都要伴随着 Redux 和 React.js 结合的库 React-redux。
专注于状态管理的库
- redux专注于状态管理,和react解耦
- 单一状态,单向数据流
- 核心概念 : store,state,action,reducer(拿到老的state和action,生成新的state)
为什么使用redux
不同的模块(组件)之间确实需要共享数据,这些模块(组件)还可能需要修改这些共享数据。这里的矛盾就是:“模块(组件)之间需要共享数据”,和“数据可能被任意修改导致不可预料的结果”之间的矛盾。我们必须通过一个“中间人” —— dispatch,所有的数据修改必须通过它,并且你必须用 action 来大声告诉它要修改什么,只有它允许的才能修改.我们再也不用担心共享数据状态的修改的问题,我们只要把控了 dispatch,所有的对 appState 的修改就无所遁形,毕竟只有一根箭头指向 appState 了。
1.安装
安装 redux 核心和 react-redux 集成进 react
$ npm install --save redux
$ npm install --save react-redux
2.API
redux store
在 redux 中 store 作为一个单例,存放了应用的所有数据,对外提供了如下的 API:
获取数据 store.getState()
通过触发 action 来更新数据 store.dispatch(action)
pubsub 模式提供消息订阅和取消 store.subscribe(listener)
创建并初始化 store
redux 提供 createStore 方法来创建一个 store
function createStore(reducer, initialState) {
// ....
}
触发 action
redux 修改应用数据的唯一方式为 store.dispatch(action)
所有对数据的操作必须通过 dispatch 函数。它接受一个参数 action,这个 action 是一个普通的 JavaScript 对象,里面必须包含一个 type 字段来声明你到底想干什么。dispatch 在 swtich 里面会识别这个 type 字段,能够识别出来的操作才会执行对 appState 的修改。
store.dispatch({
type: 'ADD_TODO',
title: 'new todo'
})
消息订阅和取消
为了让用户监听应用数据改变,redux 在 store 集成了 pubsub 模式
订阅
// 每次应用数据改变,输出最新的应用数据
store.subscribe(function(){
console.log(store.getState())
})
// 如果新增了 todo 会触发上面的订阅函数
store.dispatch({
type: 'ADD_TODO',
title: 'new todo'
})
取消
subscribe 返回的函数即为取消函数
var unsubscribe = store.subscribe(function(){
console.log(store.getState())
})
// ....
unsubscribe();
设计应用数据结构
所有数据都存放在一个 store 中,以 todoApp 为例子,state 的数据结构可能为
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
import React,{Component} from 'react'
import ReactDOM from 'react-dom'
import {
createStore
} from 'redux'
const increase = document.querySelector('.increase')
const decrease = document.querySelector('.decrease')
//创建一个store
let store = createStore(counter,0)
// 改变状态,发布一个action,每次dispath,都会重新计算reducer
document.onclick = ()=>{
store.dispatch({
type : "INCREASE"
})
}
// 监听状态改变
store.subscribe(()=>{
let state = store.getState()
console.log(state)
})
//得到reducer返回的整个应用的状态
store.getState()
console.log(store.getState())
//reducer,返回整个应用的状态
function counter(state,action){
// 取出action中的type
let {type} = action
switch (type) {
case "INCREASE" :
return state+1
break
default :
return state
}
return state
}
ReactDOM.render(
11,
document.getElementById('root')
)
创建action : actionCreators
一个函数,用来创建action,可以把业务逻辑隐藏在action里面,或者把一些异步的逻辑隐藏在它里面.
应用场景:如果有多个事件或场景下,需要共用同一逻辑,就可以封装在actionCreators里,比如点击时直接提交这个antionCreator就可以了。
let increaseAction = (val)=>{
type : "INCREASE",
val
}
increase.onclick= ()=> ({
store.dispatch(increaseAction(8))
})
// 旧的写法
increase.onclick= ()=> {
store.dispatch({
type : "INCREASE" ,
val : 8
})
}
boundActionCreators
绑定好的action creators,绑定好了dispatch
//绑定dispatch
let boundIncreace = (val)=>{
store.dispatch({
type : "INCREASE",
val
})
}
decrease.onclick = ()=>{
boundIncreace(8)
}
//旧的写法
decrease.onclick=()=>{
store.dispatch({
type : 'DECREASE',
val : 5
})
}
中间件
概念:
要做一件事情=》中间件=》完成一件事情
只要发起action,一切都尘埃落定了,就会执行dispath的逻辑,绝对不会下一秒执行,发起了,但是不执行也是不可能的。有没有一种办法,是在发起之后,再决定是否执行呢?这就需要引入中间件了。(注意 : 不应该在reducer中判断。)
也就是dispath里面可以写一个对象,也可以写一个函数。原来只能写一个对象,现在有了中间件,就可以写函数了。
react中间件 :
拦截这一次action,来决定什么时候达到reducer,决定要不要到达reducer
react-thunk:
action现在可以是一个对象,还可以是一个函数.
如果是一个对象:直接到达reducer;
如果是函数:执行函数,再决定要不要或何时到达reducer
1-下载
npm install --save redux-thunk
2-引入
import {
createStore,
applyMiddleware
} from 'redux'
import thunk from 'redux-thunk'
3-applyMiddleware函数中传入middleware)
// 如果有默认state值,放在第二个参数即可
let store = createStore(counter,applyMiddleware(thunk))
// 接收一个函数为返回值,参数是dispatch和getState
let increaseByOddAction = (val)=>(despatch,getState)=>{
if(getState()%2!==0){
dispatch(increaseAction(6))
}
}
完整计算器案例
import React,{Component} from 'react'
import ReactDOM from 'react-dom'
import {
createStore,
applyMiddleware
} from 'redux'
//引入中间件
import thunk from 'redux-thunk'
const increase = document.querySelector('.increase')
const decrease = document.querySelector('.decrease')
const odd = document.querySelector('.odd')
const async = document.querySelector('.async')
const inputVal = document.querySelector('.val')
//注册状态
let store = createStore(jsq,0,applyMiddleware(thunk))
// actionCreators
// +1
let jia = (val)=>({
type : "JIA1",
val
})
// -1
let jian = (val)=>({
type : "JIAN1",
val
})
// 提交actionCreators
// +1
let jiaDispatch = (val)=>{
store.dispatch(jsj(val))
}
// -1
let jianDispatch = (val)=>{
store.dispatch(jsjian(val))
}
// 偶数加
let jsj = (val)=>(dispatch,getState)=>{
if(getState()%2 !== 0){
dispatch(jia(val))
console.log(22)
}
}
// 基数减
let jsjian = (val)=>(dispatch,getState)=>{
dispatch(jian(val))
}
// 点击按钮提交dispatch
// 加号按钮
increase.onclick = ()=>{
jiaDispatch(1)
}
// 减号按钮
decrease.onclick = ()=>{
jianDispatch(1)
}
odd.onclick = ()=>{
jiaDispatch(4)
}
async.onclick = ()=>{
jianDispatch(8)
}
// 订阅action
store.subscribe(()=>{
let count = store.getState()
console.log(count)
inputVal.value = count
})
// reducer
function jsq(state,action){
let {type,val} = action
switch(type){
case "JIA1" :
return state+val
break;
case "JIAN1" :
return state-val
break;
default :
return state
}
return state
}
定义单独的reducer文件 : index.reducer.js
![屏幕快照 2018-06-25 下午2.35.10.png](https://upload-images.jianshu.io/upload_images/8805811-44e6e7924998a113.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)