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函数, 作用是产生状态,每二个参数是初始状态,如果有第三个参数,则是插件。只有第一个参数是必须的。
就三个方法
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
}
}
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 官网其它内容都是在讲编程规范与技巧
在上述例子中,写事件时,都是直接用字符串,比如"add",“dec”,如果多个地方用的话,会容易出错。编程中将这类容易出错的字符串叫“魔法”字符串。解决方法就是定义常量。
const TYPE_ADD="TYPE_ADD"
const TYPE_DEC="TYPE_DEC"
同理,产生事件也变成一个方法:
function createAddAction(value){
return {type:TYPE_ADD,payload:value}
}
如果有多个状态内容,比如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.subscribe只告诉状态变化了,但不知道哪个内容变化,用redux-observer可以监听指定内容。
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 将普通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,把发送事件的方法映射成组件的属性。
谢谢阅读