在我们使用react进行开发时,管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。
所以我们需要把 state 进行统一管理,这样才能控制每一个state的变化,让我们的程序可读性更强,出错率更低。
Redux 是一个 JS 库,一个状态容器。
作用: 集中式管理 React 应用中 多个组件共享的状态
(1)它可以和 react,vue,angular 等一起使用,但一般都和 react一起使用 。
(2)Redux 可以 解耦 React(View层)于数据管理和对数据的操作,保持View层的纯净,
让每一个组件的职责划分更加清楚,同时又降低了React数据传递的难度和不可控性
(3)Redux 采用了中间件机制,既保证了代码的简洁,又增加了可扩展性。
(1)单一数据源
整个应用的state都被存储到一个状态树里面,并且这个状态树,只存在于唯一的store中
(2)State 是只读的
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
(3)使用纯函数来执行修改
为了描述 action 如何改变 state tree ,你需要编写 reducers。
下面就让我们来一步步解析Redux的工作原理,以及关键功能。
(1)store就是保存数据的地方,当有数据改变时,store会调用reducer进行处理,完事后通知View层调用getState更新
(2)Redux提供createStore这个函数,用来生成Store,内部接收reducer
import {createStore} from 'redux'
const store=createStore(reducer);
(1)接收数据时,调用getState( )接收Store的newState,并重新渲染更新视图
(2)用户操作时,通过事件触发制造action并派发 来通知Store来更新state
// 接收 State
const state=store.getState()
(1)与View层的事件进行关联,一旦有通知就制造action对象
(2)action = {
type: action类型,
data: 事件携带的数据(可有可无)
}
const action={
type:'ADD_TODO',
payload:'redux原理'
}
在上面代码中,action的名称是ADD_TODO,携带的数据是字符串‘redux原理’,action描述当前发生的事情,这是改变state的唯一的方式 ( 通过action传值 )。
(1)store.dispatch()是view发出Action的唯一办法
(2)store.dispatch接收一个action作为参数,将它发送给store来通知 store调用reducer改变state。
(3)dispatch 时,还可以在中间插入副作用操作(网络请求等),其内部会调用中间件进行处理
const action={
type:'ADD_TODO',
payload:'redux原理'
}
store.dispatch(action)
// 或者直接
store.dispatch({
type:'ADD_TODO',
payload:'redux原理'
})
(1)接收两个参数(preState, action),第一个参数是之前的旧数据或初始数据,第二个参数是action
(2)处理完的State的引用地址必须为新的内存地址,否则不会触发更新**(重要)
(3)当reducer过多时,可以使用combineReducers进行整合处理
注意:Reducer必须是一个纯函数,只要传入参数相同,返回计算得到的下一个 state 就一定相同。
没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
const reducer = (preState = initialState, action)=>{
switch(action.type){
case ADD_TODO:
return newState;
default return state
}
}
(在我们实际操作时,Store只需要一开始创建就好了,后面我们我们完全不需要操作它)
源码解析参考自此
let createStore = (reducer) => {
//获取状态对象
let state;
//存放所有的监听函数
let listeners = [];
let getState = () => state;
//提供一个dispath 方法供外部调用派发action
let dispath = (action) => {
//调用管理员reducer得到新的state
state = reducer(state, action);
//执行所有的监听函数
listeners.forEach((item) => item())
}
//订阅状态变化事件,当状态改变发生之后执行监听函数
let subscribe = (listener) => {
listeners.push(listener);
}
dispath();
return {
getState,
dispath,
subscribe
}
}
let combineReducers=(renducers)=>{
//传入一个renducers管理组,返回的是一个renducer
return function(state={},action={}){
let newState={};
for(var attr in renducers){
newState[attr]=renducers[attr](state[attr],action)
}
return newState;
}
}
export {createStore,combineReducers};
// store.js
import { createStore } from "redux";
import reducer_counter from "./reducer_counter";
const store = createStore(reducer_counter);
export default store;
// constant.js
// 储存常量
export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT'
// action_creator.js
import { INCREMENT, DECREMENT } as constant from './constant'
export const incrementAction = ()=>({type: INCREMENT})
export const decrementAction = ()=>({type: DECREMENT})
// reducer_counter.js
import { INCREMENT, DECREMENT } from './actionType'
// defaultState 可以是任意js类型,但最好还是传入 数组 或 对象
const defaultState = {count: 0}
// Store收到Action以后,必须返回一个新的state(内存地址要改变),这样view才会发生变化,
const reducer_counter = (state = defaultState,action) =>{
switch (action.type) {
// 根据action.type分别对不同的action进行处理
case INCREMENT:
return {count:state.count + 1}
case DECREMENT:
return {count:state.count - 1}
default:
return state
}
}
export default reducer_counter
// App.js
import React from 'react'
import store from './store'
import {incrementAction,decrementAction} from './store/action_creator'
class App extends React.Component {
constructor(){
super()
// state就是store里面存储的数据,store里面可以拥有多个state
// Redux规定一个state对应一个View,只要state相同,view就是一样的,反过来也是一样的
// 通过store.getState()来从store获取数据
this.state = store.getState()
// 订阅state变化,state变化执行更新函数更新视图
store.subscribe(this.asyncState)
}
asyncState = ()=>{
this.setState(store.getState())
}
increment = ()=>{
// dispatch派发action,参数由action_creator创建
store.dispatch(incrementAction())
}
decrement = ()=>{
store.dispatch(decrementAction())
}
render() {
return (
<div>
Clicked : { this.state.count} times
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
);
}
}
export default App;