前言
redux核心很简单。
redux
redux核心
redux 很简单,总结起来就是一句话:一个带事件通知的状态保存者。
看代码,如里下面代码看明白了,直接跳至 插件
const {createStore} = require('redux');
function reducer(state, action) {
switch (action.type) {
case 'add':
return {value: state.value + action.payload}
case 'dec':
return {value: state.value - action.payload}
default:
return state
}
}
const store = createStore(reducer, {value: 1})
const subscriber = () => {
console.log("状态变化了", store.getState())
};
store.subscribe(subscriber)
store.dispatch({type: 'add', payload: 2})
store.dispatch({type: 'dec', payload: 1})
运行结果
状态变化了 { value: 3 }
状态变化了 { value: 2 }
代码极其简单,但redux 的所有核心(全部)内容也就在这里了。只有一个createStore函数,用来创建一个 store 来保存状态,传入的参数是一到三个。第一个参数是叫reducer函数, 作用是产生状态,每二个参数是初始状态,如果有第三个参数,则是插件。只有第一个参数是必须的。
store对象
就三个方法
- getState() , 用来取状态
- dispatch(action),告诉store 一个事件,只要是个对象就行,对象格式无要求,只要reducer认识就行。大多数按官网写法,写成 {type:‘someKey’,payload:‘someValue’} ,代表事件名称,及参数
- subscriber(callback) , 当状态变化后,通知callback , 这个通知极其简陋,只告诉你变化了,但不告诉你哪里变化了。
reducer 函数
redux 把生成状态的函数叫reducer ,接受两个参数,第一个当前状态,第二个就是事件内容。
需要注意两点
- 无论如何要返回一个状态,比如下面的例子,缺少了default 的处理,就会出错,因为少了default处理,在初始化的时候状态变成undefined
function reducer(state, action) {
switch (action.type) {
case 'add':
return {value: state.value + action.payload}
case 'dec':
return {value: state.value + action.payload}
// default: 这个注释掉就出错
// return state
}
}
- 返回的新状态必须与旧状态是不同的对象,因为redux 只是简单比较两个对象,象以下这么写,是触发不了状态变化的通知,因为返回了旧的state 对象
function reducer(state, action) {
switch (action.type) {
case 'add':
state.value = state.value + action.payload
return state // 返回了旧的state 对象
//.,..
}
}
redux 工作过程
初始化
createStore 调用reducer ,传入一个参数 :初始状态,也就是createStore的第二个参数。调用后,得到初始状态,保存下来。
运行时
三步曲:
用dispatch 发一个事件 => store调用reducer=>发出通知 subscribe
插件
插件就是拦截 store.dispatch 方法,写法是固定的,下面以一个日志插件为例
function logger({dispatch, getState}) {
//此dispatch 方法是未被拦截的dispatch
return (next) => {
// next 是指下一个插件
return (action) => {
//需要填写的内容在此
console.log('before log', action, getState())
next(action)
console.log('after log', getState)
}
}
}
const store = createStore(reducer,applyMiddleware(logger))
插件可以形成一个串行调用链。上面例子中的 applyMiddleware 方法 由redux提供,参数是可以多个插件,作用就是将多个插件串起来。
redux 其它内容
redux 官网其它内容都是在讲编程规范与技巧
取消“魔法”字符串和对象
在上述例子中,写事件时,都是直接用字符串,比如"add",“dec”,如果多个地方用的话,会容易出错。编程中将这类容易出错的字符串叫“魔法”字符串。解决方法就是定义常量。
const TYPE_ADD="TYPE_ADD"
const TYPE_DEC="TYPE_DEC"
同理,产生事件也变成一个方法:
function createAddAction(value){
return {type:TYPE_ADD,payload:value}
}
combineReducers 合并多个reducer
如果有多个状态内容,比如user 的状态,post 的状态,如果都写在一个文件里,会太庞大。所以,用combineReducers这个方法合并
function userReducer(state, action) {
//省略
}
function postReducer(state, action) {
//省略
}
const reducer = combineReducers({user: userReducer, post: postReducer})
这样产生出来的state 就象:
{
user:{}
post:{}
}
异步调用
确切的说 :redux 并不支持异步,因为reducer必须是同步的。store接收到dispatch过来的action后,直接调用reducer,得到结果,这个机制无法改变。所有的异步,都得发生在dispatch之前。所以,异步得自己管。
有几个插件,可以协助异步,原理都是拦截dispatch方法,根据action类型或者内容,执行相应操作。
- redux-thunk 让action 可以是函数
- redux-saga 监听action.type ,接管特定action ,触发一些异步动作
- redux-promise 让action 可以是Promise对象
监听结果
redux.subscribe只告诉状态变化了,但不知道哪个内容变化,用redux-observer可以监听指定内容。
react-redux 粘合剂
redux 与react 结合时,不用 react-redux 也可工作。状态改变后,用store.subscriber 通知react重渲染
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from "redux";
function reducer(state, action) {
switch (action.type) {
case 'add':
return {value: state.value + action.payload}
case 'dec':
return {value: state.value - action.payload}
default:
return state
}
}
const store = createStore(reducer, {value: 1})
const subscriber = () => {
ReactDOM.render(
<div>
<div>{store.getState().value}</div>
<button onClick={() => store.dispatch({type: 'add', payload: 1})}>加1</button>
<button onClick={() => store.dispatch({type: 'dec', payload: 1})}>减1</button>
</div>,
document.getElementById('root')!
)
};
store.subscribe(subscriber)
这样写,主要的缺陷就是组件与store 耦合紧密,不利于调试与复用,所以就用到react-redux 来解耦与粘合。
Provider 与 connect
主要就两个内容:Provider 组件与 connect 方法。 Provider 用来提供全局的状态提供者,connect 将普通Component包裹一下,能够接收Provider提供的状态。
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from "redux";
import {Provider,connect} from 'react-redux'
function reducer(state, action) {
switch (action.type) {
case 'add':
return {value: state.value + action.payload}
case 'dec':
return {value: state.value - action.payload}
default:
return state
}
}
const store = createStore(reducer, {value: 1})
//以下开始不一样
class Counter extends Component{
render(){
return(
<div>
<div>{this.props.value}</div>
<button onClick={this.props.addMethod}>加1</button>
<button onClick={this.props.decMethod}>减1</button>
</div>
)
}
}
function mapStateToProps(state){
return {
value:state.value
}
}
function mapDispatchToProps(dispatch){
return {
addMethod:()=>dispatch({type: 'add', payload: 1})
decMethod:()=>dispatch({type: 'dec', payload: 1})
}
}
const WrapedCounter=connect(mapStateToProps,mapDispatchToProps)(Counter)
ReactDOM.render(
(
<Provider store={store}>
<WrapedCounter/>
</Provider>
),
document.getElementById('root')
)
connect 方法需要两个函数参数,一个是mapStateToProps ,把store中的状态映射到组件的属性。一个是mapDispatchToProps,把发送事件的方法映射成组件的属性。
总结
谢谢阅读