2021年2月9日星期二

《深入浅出React和Redux》(2)

### Redux是Flux理念的一种实现。关于Flux理念可以通过类比MVC模式来做简单理解。MVC模式中,用户请求先到达Controller,由Controller调用Model获得数据,然后把数据交给View,按照这种模式,MVC应该也是一个controller->model->view的单向数据流,但是,在实际应用中,由于种种原因,往往会让view直接操作Model,随着应用的演进、逻辑变得越来约复杂,view与model之间的关系就会变得错综复杂、难以维护。**在MVC中让View和Model直接对话就是灾难**。Flux理念可以简单看作是对MVC添加了更加严格的数据流限制。Flux框架随React一同被Fackbook推出,但在Dan Abramov创建了Redux后,Redux已经替代了Flux。### 基本原则Flux的基本原则是"单向数据流", Redux在此基础上强调三个基本原则:- 唯一数据源(Single Source of Truth),应用的状态数据应该只存储在唯一的一个Store上,它是树形结构,每个组件往往只是用树形对象上一部分的数据,而如何设计Store上状态的结构,就是Redux应用的核心问题。- 保持状态只读(State is read-only),不能直接修改store状态,必须要通过派发action对象的方式来进行。- 数据改变只能通过纯函数完成(Changesare made with pure functions)。这个纯函数就是Reducer, Dan Abramov说过,Redux名字的含义就是Reducer+Flux。Reducer是一个很多语言都支持的函数,下面是js中数组的reduce函数的用法:

[1,2,3,4].reduce(function reducer(accumulation, item){
return accumulation + item;
}, 0)

reduce函数的第一个参数是reducer, 这个函数把数组所有元素依次做"规约",对每个元素都调用一次reducer,通过reducer函数完成规约所有元素的功能。在Redux中,reducer的函数签名为:

reducer(state, action)

它根据state和action的值产生一个新的state对象返回,reducer是纯函数,只负责计算状态,不会去存储状态。### Redux的使用使用create-react-app创建的模版中不包含redux依赖,首先需要执行```npm install redux```安装。MVC中标准的单向数据流为controller->model->view的,对应地,在React配合Redux后,要触发view的更新,需要发出一个action,然后reducer根据action来更新store的状态,最后让view根据store中最新的数据来更新。#### ActionRedux应用习惯上把action类型和action构造函数分成两个文件定义,两者的内容类似这样:**ActionTypes.js**

export const INCREMENT="increment";
export const DECREMENT="decrement";

**Actions.js**这里的每个action构造函数都返回一个action对象

import * as ActionTypes from './ActionTypes.js';

export const increment = (counterCaption) => {
return {
type: ActionTypes.INCREMENT,
counterCaption: counterCaption
}
};

export const decrement = (counterCaption) => {
return {
type: ActionTypes.DECREMENT,
counterCaption: counterCaption
}
};

#### Store**Store.js**举例

import { createStore } from 'redux';
import reducer from './Reducer.js';

const initValues = {
'First': 0,
'Second': 10,
'Third': 20
}

const store = createStore(reducer, initValues);

export default store;

store的创建要调用Redux库提供的createStore函数:- 第一个参数为reducer,它负责更新更新状态- 第二个参数是状态的初始值- 还有第三个参数为Store Enhancer,后面再了解确定Store状态,是设计好Redux应用的关键,其主要原则是:**避免冗余的数据**;#### Reducer**Reducer.js**举例

import * as ActionTypes from './ActionTypes.js';

export default (state, action) => {
const { counterCaption } = action;

switch (action.type) {
case ActionTypes.INCREMENT:
return { ...state, [counterCaption]: state[counterCaption] + 1 };
case ActionTypes.DECREMENT:
return { ...state, [counterCaption]: state[counterCaption] - 1 };
default:
return state;
}
}

Reducer的主要结构就是if-else或switch语句,根据action.type来执行对应的reduce操作。```...state```为扩展操作符语法,将state字段扩展开赋值给一个新的对象,再根据counterCaption修改对应的字段,这种简化写法等同于:

const newState = Object.assign({}, state);
newState[counterCaption]++;
return newState;

扩展操作符语法(spread operator)并不是ES6语法,但因其语法简单而被广泛使用,babel会负责解决兼容性问题。reducer是纯函数,不会修改原有的state,而是操作新复制的state。### Viewview代码举例:

import { Component } from 'react';
import PropTypes from 'prop-types';
import store from '../Store.js';
import * as Actions from '../Actions.js'

class Counter extends Component {
constructor(props) {
super(props);
/* bind methods*/

this.state = this.getOwnState();

}

getOwnState() {
return {
value: store.getState()[this.props.caption]
}
}

onChange() {
this.setState(this.getOwnState());
}

onClickIncrementButton() {
store.dispatch(Actions.increment(this.props.caption));
}

onClickDecrementButton() {
store.dispatch(Actions.decrement(this.props.caption));
}

componentDidMount() {
store.subscribe(this.onChange);
}

componentWillUnmount() {
store.unsubscribe(this.onChange);
}

render() {
const { caption } = this.props;
const value = this.state.value;

return ( <div> <button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button> <button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button> <span>{caption} count:{value}</span> </div>);

}
}

- ```store.getState()```获取store上存储的所有状态,当前组件只需要获取自身相关的状态信息;- componentDidMount中,通过store.subscribe监听store状态的变化;store状态变化时,触发onChange,在这里会调用```this.setState```触发view更新;- 此外还要在componentWillUnmount中取消对store状态变化的监听;- 点击button时,通过store.dispatch分发一个action,这个action由Actions.js中的某个action构造函数产生。### 容器组件和展示组件从前文的Redux示例代码可以发现,在Redux框架下,一个React组件基本上就是要完成以下两个功能:- 与store相关的职责: - 读取并监听Store状态的改变; - 当Store状态变化时,更新组件状态,驱动组件重新渲染; - 当需要更新Store状态时,派发action对象;- 根据当前props和state,渲染出用户界面。于是考虑将组件一分为二:- 容器组件:承担store相关职责- 展示组件:只关注页面渲染展示组件没有状态,是一个纯函数,只需要根据props来渲染,不需要包含state;### 组件Context但是如果每个组件都拆分为容器组件和展示组件后,由于容器组件要与store打交道,那么每个容器组件都需要导入store,最好能只在顶层的组件导入一次,子组件就都可以用了,不过子组件使用时不是使用props层层传递,而是借助React提供的Context(上下文环境)。要使用Context需要上下级组件之间的配合,首先来看顶层组件。在此创建一个Provider组件作为一个通用的Context提供者

import { Component } from 'react';
import PropTypes from 'prop-types';

class Provider extends Component {
getChildContext() {
return {
store: this.props.store
};
}

render() {
return this.props.children;
}
}

Provider.childContextTypes = {
store: PropTypes.object
}

Provider.propTypes = {
store: PropTypes.object.isRequired
}

export default Provider;

这个组件作为父组件使用:

···
import store from './Store.js';
import Provider from './Provider.js';

ReactDOM.render(


,
document.getElementById('root')
);

顶层的Provider组件,通过```Provider.childContextTypes```来宣称自己支持context,并且提供```getChildContext```函数来返回代表Context的对象,Context对象中存储的值实际上是index.js导入的store,作为props传递给了Provider。这样就实现了只在index.js导入store一次。Provider组件还会通过```this.props.children```把子组件渲染出来。然后它子孙组件,只要宣称自己需要这个context,就可以通过this.context访问到这个共同的环境对象。就像这样:

SummaryContainer.contextTypes = {
store: PropTypes.object
}

另外,构造函数也要传递context:

constructor(props, context) {
super(props, context);
···
}

这里可以进一步简化为:

constructor() {
super(...arguments);
···
}

### React-Redux以上过程从两个方面改进了React应用:- 把组件拆分为容器组件和展示组件- 所有组件都通过Context来访问store实际上使用react-redux库可以代替上面的大部分工作了,不过通过前面一步步的改进过程可以了解其中的原理。引入react-redux后,代码相比之前会更加简洁。首先在index.js修改为从react-redux导入Provider:

import {Provider} from 'react-redux';

在子组件中,容器组件部分也交给react-redux来创建,我们只需编写展示组件部分,并定义好mapStateToProps和mapDispatchToProps。之前在容器组件有定义getOwnState方法,将store上的状态转化为展示组件的props

getOwnState() {
return {
value: this.context.store.getState()[this.props.caption]
}
}

mapStateToProps也是同样的作用:

function mapStateToProps(state, ownProps) {
return {
value: state[ownProps.caption]
}
}

容器组件还定义了点击是分发action的函数,

onClickIncrementButton() {
this.context.store.dispatch(Actions.increment(this.props.caption));
}

onClickDecrementButton() {
this.context.store.dispatch(Actions.decrement(this.props.caption));
}

mapDispatchToProps的也是同样的作用,将dispatch传递给展示组件的props,供其主动触发:

function mapDispatchToProps(dispatch, ownProps) {
return {
onIncrement: () => {
dispatch(Actions.increment(ownProps.caption));
},
onDecrement: () => {
dispatch(Actions.decrement(ownProps.caption));
}
}
}

mapStateToProps和mapDispatchToProps会作为参数传递给react-redux的connect

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

connect函数的运行结果还是一个函数,然后把展示组件Counter作为参数调用这个函数,这个过程就类似于之前的容器组件嵌套展示组件。### 参考书籍《深入浅出React和Redux》 程墨








原文转载:http://www.shaoqun.com/a/540605.html

跨境电商:https://www.ikjzd.com/

ola:https://www.ikjzd.com/w/2103

智赢:https://www.ikjzd.com/w/1511


###Redux是Flux理念的一种实现。关于Flux理念可以通过类比MVC模式来做简单理解。MVC模式中,用户请求先到达Controller,由Controller调用Model获得数据,然后把数据交给View,按照这种模式,MVC应该也是一个controller->model->view的单向数据流,但是,在实际应用中,由于种种原因,往往会让view直接操作Model,随着应用的演
雨果网:雨果网
wangwei:wangwei
亚马逊新手卖家的选品流程:亚马逊新手卖家的选品流程
转发!亚马逊日本站家居类卖家遭大卖恶意欺凌,愤起曝光店铺信息!:转发!亚马逊日本站家居类卖家遭大卖恶意欺凌,愤起曝光店铺信息!
玩转亚马逊Listing必看:玩转亚马逊Listing必看

没有评论:

发表评论