React之React-Redux
实际项目中,需要权衡是直接使用 Redux 还是用
React-Redux
。
关于Redux
Redux 与 React-Redux 区别
与redux
比较,React-redux
更为简洁,只要在mapStateToProps
中绑定的state
或者action
就直接可以在组件的props
中获取到,这样state
与action
在mapStateToProps
统一管理,使用起来很方便。
更重要的是,React-Redux
自己会监听state
的变化进行更新,而redux
需要手动的在生命周期componentDidMount
中通过store.subscribe()
来订阅事件 更新state
。
关于UI 组件与容器组件
React-Redux
将所有组件分成两大类:UI 组件 和 容器组件
UI 组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 没有状态(即不使用this.state这个变量)
- 所有数据都由参数(this.props)提供
- 不使用任何 Redux 的 API
容器组件
- 负责管理数据和业务逻辑,不负责 UI 的呈现
- 带有内部状态
- 使用 Redux 的 API
UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。
如果一个组件既有 UI 又有业务逻辑,那怎么办?
回答是,将它拆分成下面的结构:外面是一个容器组件,里面包了一个UI 组件。
前者负责与外部的通信,将数据传给后者,由后者渲染出视图。
React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。
也就是说,用户负责视觉层,状态管理则是全部交给它。
安装React-Redux
npm install react-redux --save
或
yarn add react-redux
使用React-Redux
1. Provider组件
React-Redux 提供Provider
组件,Provider
包裹在根组件外层,使所有的子组件都可以拿到 state
Provider
连接了store
Provider
内部的所有组件都可以使用store
在 src/index.js
中引入Provider:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux'; // 引入Provider
import store from './redux/store'; // 引入store
ReactDOM.render(
<React.StrictMode>
{/* 使用Provider并加载数据仓库 */}
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
.
2. connect
React-Redux
提供connect
方法,用于从 UI 组件生成容器组件。connect
的意思,就是将这两种组件连起来。
import { connect } from 'react-redux'
// ...
export const VisibleList = connect()(List);
List
是 UI 组件,VisibleList
就是由 React-Redux
通过 connect
方法自动生成的容器组件。
.
3. mapStateToProps和mapDispatchToProps
connect
方法接受两个参数:mapStateToProps
和mapDispatchToProps
。它们定义了 UI 组件的业务逻辑。
3.1 mapStateToProps()
负责输入逻辑,即将state映射到 UI 组件的参数(props)
mapStateToProps()
是一个函数,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。
该函数返回一个对象,里面的每一个键值对就是一个映射。
每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
// state就是数据仓库store的数据
const mapStateToProps = state => {
return {
myValue: state.myValue
};
};
把state
数据映射到props
中,这样在 jsx 中就可以用this.props.myValue
来代替this.state.myValue
获取值
因此就不需要在构造函数中初始化state,也无需订阅store:
constructor(props) {
super(props);
// 因此就不需要在构造函数中初始化这个state
// const storeState = store.getState();
// this.state = {
// myValue: storeState.myValue
// }
// 也无需订阅store
// store.subscribe(this.storeChange.bind(this));
}
3.2 mapDispatchToProps()
负责输出逻辑,即将用户对 UI 组件的操作映射成 Action
mapDispatchToProps()
作为函数,返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。
const mapDispatchToProps = dispatch => {
return {
changeInput: (e) => {
const action = {
type: 'change_input',
value: e.target.value,
};
dispatch(action); // 分发action
},
// ...
};
};
把store.dispatch()
挂载到props
上,这样在 jsx 中就可以用this.props.changeInput
来代替store.dispatch()
改变store
里的数据:
// changeInput = (e) => {
// const action = {
// type: 'change_input',
// value: e.target.value,
// };
// store.dispatch(action); // 用 this.props.changeInput 来代替 store.dispatch() 改变 store 里的数据
// }
3.3 connect(mapStateToProps, mapDispatchToProps)
connect
方法接受mapStateToProps
和mapDispatchToProps
export const VisibleList = connect(mapStateToProps, mapDispatchToProps)(List);
简单的例子
将上面几个部分整合一下:
src/List.js
import { Component } from 'react';
import { connect } from 'react-redux';
class List extends Component {
render() {
return (
<div>
<label htmlFor="input">输入内容</label>
<input id="input" type="text" value={this.props.myValue} onChange={this.props.changeInput} />
</div>
);
}
}
// 把state数据映射到props中
// 这样在jsx中就可以用this.props.myValue来代替this.state.myValue获取值
const mapStateToProps = state => {
return {
myValue: state.myValue,
};
};
// 把store.dispatch()挂载到props上
// 这样在jsx中就可以用this.props.changeInput来代替store.dispatch()改变store里的数据
const mapDispatchToProps = dispatch => {
return {
changeInput(e){
const action = {
type: 'change_input',
value: e.target.value,
};
dispatch(action);
}
};
};
// List是 UI 组件,VisibleList 就是由 React-Redux 通过connect方法自动生成的容器组件。
export const VisibleList = connect(mapStateToProps, mapDispatchToProps)(List);
注:以上类组件中的connect(mapStateToProps, mapDispatchToProps)
使用较为繁琐,可以使用函数式组件中的hooks来替代,传送门
src/store/reducer.js
const defaultState = {
myValue: ''
};
const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'change_input':
return { ...state, myValue: action.value }; // 不能直接修改旧的state,而是返回一个新的state来替代
default:
return state;
}
};
export default reducer;
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux'; // 引入Provider
import store from './redux/store'; // 引入store
ReactDOM.render(
<React.StrictMode>
{/* 使用Provider并加载数据仓库 */}
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
很重要,一定要掌握
end~