0%

redux 学习笔记

Redux

Flux演变而来,作为状态容器,提供可预测的状态管理。

跨组件,多层级组件,想要 需要共享的数据,可以使用。

能不用尽量不用。

redux 库可以脱离 React 应用使用。

安装Redux- DevTools

chrome 商店 收索 redux dev 即可安装。
安装之后还不可以使用。
创建 store 时,添加以下代码
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()

1
2
3
4
let store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)

基本概念

state

State 是只读的。

Store对象包含所有数据。由 store 管理且由getState()方法获得。它表示了 Redux 应用的全部状态。

state 可以是任意的数据类型。然而你应尽可能确保 state 可以被序列化,而且不要把什么数据都放进去,导致无法轻松地把 state 转换成 JSON。

Action

Action是一个用来描述修改 state 的操作。通过 dispatch 告诉 store , state 如何修改。

action只是一个传递信息的,他不能改变store。store 只能被 reducer改变。

action必须有一个type属性,表明即将执行的action的类型,type通常被定义为一个字符常量。action对象中的其他属性是携带的信息。

1
2
3
4
const action = {
type: 'ADD_TODO',
payload: 1
};

上面代码中,Action 的名称是ADD_TODO,它携带的信息是number: 1。

Action Creator

action creator是创建action的函数的工厂,简单地返回action,它能根据传入的参数设置action的属性值。

1
2
3
4
5
6
7
8
9
const ADD_TODO = '添加 TODO';

function addTodo(number) {
return {
type: ADD_TODO,
number
}
}
const action = addTodo(3);

Reducer

当store 收到 Action 以后,必须给出一个新的 State。这种 State 的计算过程就叫做 Reducer。

它要做的仅仅是 —— 负责初始 state,当 store 收到 Action 以后,根据 state 和 action 返回新的 state。这种 State 的计算过程就叫做 Reducer。

可以理解为一个专门处理state的工厂 给他一个旧数据它会根据不同action.type返回新的数据 也就是:旧state + action = 新state,每个项目有且可以有多个reducer。

Reducer 是一个纯函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。

纯函数:

  1. 传入相同的参数会返回相同的结果。
  2. 执行纯函数不会造成副作用。(这里的副作用指的是函数改变了作用域之外的状态值,比如函数外部定义了变量a,在函数内部改变了变量a的值。)

可用 immutable.js来保证数据的不可变性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
根据传入的 action 的 type 不同,返回一个新的 state 数据
*/
// 先初始化 state
const initCounter = 0;
const reducer = function (state = initCounter, action) {
const { number } = action;
let data = {...state};
switch (action.type) {
case 'ADD_NUMBER':
return data + number
default:
return state
}
};

reducer 函数 不是修改 state,而是 深度拷贝 state得到新对象,来操作这个新对象并当作 新的 state 返回。

使用 ES7 的{ ...state }Object.assign({},state,{})实现浅拷贝满足使用

JSON.parse(JSON.stringify(state)) 对象的深度拷贝

default 情况下返回旧的 state遇到未知的 action 时,一定要返回旧的 state

combineReducers

合并 多个 reducer 的方法。

一个 reducer 处理 同一个 state,不可避免的 导致 state 过于庞大 和 reducer 方法臃肿。

把 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state的一部分。

combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
let data = {
'1': {
arr:[1,2,3],
number:1,
},
'2':{
msg: 'hello'
}
}
let reducerOne = (state=data[1],action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'ADD_NUMBER':
data.number += action.number;
return data;
case 'ADD_ARR':
data.arr.push(action.number);
return data;
default:
return data;
}
}

let reducerTwo = (state=data[2],action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'ADD_STRING':
data.msg += action.msg;
return data;
default:
return data;
}
}
let reducer = combineReducers({
reducerOne,reducerTwo
});
/*
当你触发 action 后,combineReducers 返回的 reducer 会负责调用reducerOne,reducerTwo
*/
const store = createStore(reducer);

console.log(store.getState())
/*
{reducerTwo: {…}, reducerOne: {…}}
reducerOne: {arr: Array(3), number: 1}
reducerTwo: {msg: "hello"}
__proto__: Object
*/

dispatch

dispatch 是一个接收 action 的函数,往 store 分发一个或多个 action,要么不分发任何 action。

1
2
3
4
5
6
store.dispatch({
type: 'ADD_TODO',
number: 5
});
// 或者
store.dispatch(addTodo(5));

createStore

创建store 的方法。

createStore(reducer, preloadedState?, enhancer?)

  • 上面的 reducer 函数,必须有.
  • preloadedState 可选参数,初始时的 state。 如果你使用 combineReducers 创建 reducer,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何 reducer 可理解的内容。
  • enhancer 可选参数,store的增强器,顾名思义,就是增强store的功能。比如 使用第三方插件,react-thunk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
let reducerOne = (state={},action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'ADD_NUMBER':
data.number += action.number;
return data;
default:
return data;
}
}
let reducerTwo = (state={},action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'ADD_STRING':
data.msg += action.msg;
default:
return data;
}
}
/*
combineReducers 创建的reducer,在createStore 中初始化 state 时,
属性值 是 变量时,必须重新起属性名,属性值和属性名不能相同。
像这样 是不能正常初始化 state

let reducer = combineReducers({
reducerOne,reducerTwo
});
let data = {
'reducerOne':{
msg: 'hello'
},
'reducerTwo': {
arr:[1,2,3],
number:1,
},
}
*/
let reducer = combineReducers({
'one':reducerOne,
'two':reducerTwo
});
let data = {
'one':{
msg: 'hello'
},
'two': {
arr:[1,2,3],
number:1,
},
}
const store = createStore(reducer,data);

store

Store 就是保存数据的地方,一个仓库。整个应用只能有一个 Store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合而不是创建多个 store。

Store将actions和reducers结合起来,store的功能是:

  1. 维持应用的 state

  2. 通过getState()拿到state。

  3. 通过dispatch(action)更新state。

  4. 通过subscribe(listener)注册监听器。listener是一个函数,当发送action的时候会执行listener。

    每次state变更时,都会触发其订阅的事件 listener。

  5. 执行subscribe(listener)会返回 一个函数,调用这个函数能够注销监听器。

通过 创建一个 store,传入 reducer,每当我们在 store 上 dispatch 一个 action,reducer 会根据 action的 type ,做修改 state 的操作,store 内的数据就会相应地发生变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 创建 一个 store,传入 reducer
const store = createStore(reducer);

// 组件中 注册 subscribe(listener) 绑定 用于 setState 的函数
class App extends React.Component{
constructor(props) {
super(props);
this.unsubscribe = store.subscribe(this.changeStore);
}
state = {
num: store.getState().reducerOne.number,
}
changeStore = ()=>{
this.setState({num:store.getState().reducerOne.number});
}

componentWillUnmount() {
this.unsubscribe(); //注销监听器
}

handClick = () =>{
store.dispatch({
type:'ADD_NUMBER',
number: 1,
})
}
render() {
return <div>
<button onClick={this.handClick}>点击</button>
<p>{this.state.num}</p>
</div>
}
}
ReactDOM.render(<App />,document.getElementById('app'))

Redux-thunk — 中间件(middleware)

中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。

也就是 改变action -> reducer 的过程。变为 action -> middlewares(中间件) -> reducer 。使用它改变数据流,实现异步 action .

Action 发出以后,Reducer 立即算出 State,这叫做同步。Action 发出以后,过一段时间再执行 Reducer,这就是异步。

redux-thunk中间件改造了redux的dispatch方法允许我们用store.dispatch(fn), fn可以是一个函数。而且此函数可以接受两个参数:dispatchgetState做为参数。

也就是 store.dispatch()store.getState() 的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'

// reducer
let data = {};
let reduce = (state=data,action) =>{
let data = JSON.parse(JSON.stringify(state));
switch (action.type) {
case 'GET_LIST':
data.list = action.list;
return data;
default:
return data;
}
};

// 解决redux 和 redux devtools 的冲突问题
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
// 加载 redux-thunk
const enhancer = composeEnhancers(applyMiddleware(thunk));

const store = createStore(reducer,enhancer);


//action
function incrementIfOdd() {
return async (dispatch, getState) => {
let list = await fetch(
'http://rap2.taobao.org:38080/app/mock/254619/redux-thunk'
).then(res => res.json());

dispatch({
type: 'GET_LIST',
list
});
console.log(getState());
/*
{
arr: (3) [1, 2, 3]
msg: "hello"
}
*/
}
}

React-Redux

react-redux是Redux 的作者封装了一个 React 专用的库 React-Redux

Redux 本身和React没有关系,只是数据处理中心,是React-Redux让他们联系在一起。

这个库是可以选用的。实际项目中,你应该权衡一下,是直接使用 Redux,还是使用 React-Redux。后者虽然提供了便利,但是需要掌握额外的 API,并且要遵守它的组件拆分规范。

React-Redux 将所有组件分成两大类:UI 组件和容器组件

  1. 前者会处理逻辑
  2. 后者只负责显示和交互,内部不处理逻辑,状态完全由外部掌控

两个核心概念

Provider 的组件

将顶层组件包裹在Provider组件之中,这样的话,所有组件就都可以在react-redux的控制之下了,但是store必须作为参数放到Provider组件中去

1
2
3
<Provider store = {store}>
<App />
<Provider>

通过用 Provider 组件包装整个应用,App 组件的所有子组件都可以访问 Redux store。

connect(stateProps, dispatchProps)(ComponentName)

映射器,在需要用到 state 或 dispatch(action) 的组件中使用 connect 函数( 即高阶组件) 进行包装。

可以看到connect(stateProps, dispatchProps)(ComponentName) 调用了两次。

其实connect 是一个高阶函数,它简单说就是当你调用它时会返回一个函数。然后调用返回的函数传入一个组件时,它会返回一个新(包装的)组件

stateProps(state, ownProps?)

它是个自定义函数,从Redux 状态树中提取需要的state 作为props传递给当前的组件。

所以他的作用就是其实也就是当 redux 的state 改变,props 改变重新 渲染依赖组件。

stateProps 结果一定要返回一个object 。

  • 第一个参数 state, 相当于 store.getState() 的封装,但是 你可以只获取 state 中的任意一个或多个数据。
  • 第二个可选 参数ownProps ,父组件 传递给 组件 的 props。

dispatchProps(dispatch, ownProps?)

把 组件内 分发 action 的方法 当作 props 。

  • dispatch参数: 把 store.dispatch() 方法封装,直接调用 dispatch。
  • ownProps,父组件 传递给 组件 的 props。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function Bt(props) {
return (
<div>
<button onClick={props.handClick}>点击</button>
<p>{props.number}</p>
</div>
)
}
let stateProps = state => {
return {
number: state.number
}
}
let dispatchProps = dispatch =>{
return {
handClick: e => dispatch({
type:'ADD_NUMBER',
number: 1,
})
}
}

// connect()() 返回的是一个组件
let Button = connect(stateProps,dispatchProps)(Bt);

class App extends React.Component{
constructor(props) {
super(props);
}

render() {
return (
<Provider store = {store}>
<Button></Button>
</Provider>
)}
}
ReactDOM.render(<App/>,document.getElementById('app'))

Redux系列之分析中间件原理(附经验分享)
Redux 中文文档
Redux 入门到高级教程
[译] 2019 React Redux 完全指南
react-redux一点就透,我这么笨都懂了!
React Redux
一篇文章总结redux、react-redux、redux-saga