react-redux

一、简介

react 开发的项目中,组建直接传递参数或者事件都需要props一层层代理,对于复杂组件,它可能嵌套的子组件非常多,层级也比较深,那么,如果还采用props链条来维护组件通信或者数据共享,将非常困难,也不利于开发和维护。

所以,当有多个组件需要共享状态/数据时,可以使用官方的一个状态管理库 react-redux

从一开始的 Flux ,演变成 Redux ,而 Redux 并不是专为 React 开发的,它可以应用在任何框架上,而 react-redux 的出现, 是为了在 react 中更好的使用它。

二、概念

2.1、UI 组件

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 没有状态(即不使用 this.state 这个变量)
  • 所有数据都由参数 (this.props) 提供
  • 不使用任何 ReduxAPI

2.2、容器组件

  • 负责管理数据和业务逻辑,不负责 UI 的呈现
  • 带有内部状态
  • 使用 ReduxAPI

2.3、connect()

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect 的意思,就是将这两种组件连起来。

三、基本使用

以一个计数器为例

编写一个 UI 组件,用 connect 方法将其生成容器组件。

3.1 组件

src/container/Counter/index.js

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 React, { Component } from "react";
// connect,用连接 UI 组件和容器组件
import { connect } from "react-redux";
// 引入 action
import { handleIncreaseAction, handleInReduceAction } from './action'

class Counter extends Component {
render() {
// 从 props 项接收一个值和两个方法
const { value, onIncreaseClick, onReduceClick } = this.props;
return (
<div>
<span>{value}</span>
<button onClick={onIncreaseClick}>增加</button>
<button onClick={onReduceClick}>减少</button>
</div>
);
}
}

// mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。
function mapStateToProps(state) {
return {
value: state.counterReducer.count
};
}

// Map Redux actions to component props
function mapDispatchToProps(dispatch) {
return {
onIncreaseClick: () => {
dispatch(handleIncreaseAction());
},
onReduceClick: () => {
dispatch(handleInReduceAction());
}
};
}

// connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。
export default connect(
mapStateToProps,
mapDispatchToProps
)(Counter);

3.1.1 bindActionCreators

​ Redux 中的 bindActionCreators,是通过 dispatch 将 action 包裹起来,这样可以通过 bindActionCreators 创建的方法,直接调用dispatch(action)(隐式调用)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { bindActionCreators } from 'redux';
// 将所有的 action 引入,内部包含 setTheme 方法
//export function setTheme(themeName) {
// return {
// type: SET_THEME,
// themeName
// };
//}
import * as ThemeActions from '../../actions/theme';

// 只需调用一个方法时用以下写法
function mapDispatchToProps(dispatch) {
return bindActionCreators(ThemeActions, dispatch);
}
// 需调用二个方法时用以下写法
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(ReplyKindActions, dispatch),
...bindActionCreators(ThemeActions, dispatch)
};
}
// 调用
const { setTheme } = this.props;
setTheme(e.target.value);

3.2 action

在容器组件中使用 dispatch 向外请求一个 actiontype 字段是必须要有的,类似于后端的接口 url 地址

src/container/Counter/action.js

1
2
3
4
5
6
7
8
9
10
11
12
13
export function handleIncreaseAction(num) {
return {
type: "increase",
num: num
};
}

export function handleInReduceAction(num) {
return {
type: "inReduce",
num: num
};
}

3.2.1 规范

在 action 中定义的 type ,最好使用常量定义,供 reduce 直接调入使用。

action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';

export function increment() {
return {
type: INCREMENT_COUNTER
};
}

export function decrement() {
return {
type: DECREMENT_COUNTER
};
}

reducer

1
2
3
4
5
6
7
8
9
10
11
12
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter';

export default function counter(state = 0, action) {
switch (action.type) {
case INCREMENT_COUNTER:
return state + 1;
case DECREMENT_COUNTER:
return state - 1;
default:
return state;
}
}

3.3 reducer

reducer 中对数据进行修改操作

src/container/Counter/reducer.js

1
2
3
4
5
6
7
8
9
10
11
12
13
const counterReducer = (state = { count: 0 }, action) => {
const count = state.count;
switch (action.type) {
case "increase":
return { count: count + 99 };
case "inReduce":
return { count: count - 99 };
default:
return state;
}
};

export default counterReducer;

3.4 组合所有的 reducer

src/reducers.js

1
2
3
4
5
6
7
8
9
import { combineReducers } from "redux";

import counterReducer from "./containers/Counter/reducer";

export default function createReducer() {
return combineReducers({
counterReducer
});
}

3.5 创建 store

src/configureStore.js

1
2
3
4
5
6
7
8
import { createStore } from "redux";
// 引入所有的 reducer
import createReducer from './reducers';

export default function configureStore() {
const store = createStore(createReducer());
return store;
}

3.6 <Provider> 组件

使用 React-Redux 提供 Provider 组件,可以让容器组件拿到 state

src/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from "react";
import ReactDOM from "react-dom";
import registerServiceWorker from "./registerServiceWorker";
import CRouter from "./router";

// redux
import { Provider } from "react-redux";
import configureStore from "./configureStore";

const store = configureStore();

ReactDOM.render(
<Provider store={store}>
<CRouter/>
</Provider>,
document.getElementById("root")
);
registerServiceWorker();

四、调试

在开发的过程中,为了更加方便地实时查看到 state 状态,可以使用 redux-devtools-extension

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// compose 用来组和插件和中间件
import { createStore, applyMiddleware, compose } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import createReducer from './reducers';

let middleware = []
// createStore 第一个参数为 reducer
// 调试插件需要做判断,有的浏览器有安装,没有安装的会报错
// TypeError: Unable to get property 'apply' of undefined or null reference
var store;
if (window.__REDUX_DEVTOOLS_EXTENSION__) {
store = createStore(createReducer(), compose(applyMiddleware(...middleware), composeWithDevTools()))
} else {
store = createStore(createReducer(), applyMiddleware(...middleware))
}

export default function configureStore() {
return store;
}

在谷歌应用商店中下载 redux-devtools 插件

效果图

image

Redux 入门教程(三):React-Redux 的用法 — 阮一峰

本文结束,感谢您的阅读