11.2.4 集成Redux
您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!
11.2.4 集成Redux
为了实现路由功能,有React-Router就足够,和Redux并没有什么关系。但是我们依然希望用Redux来管理应用中的状态,所以要把Redux添加到应用中去。
首先在src/Store.js中添加创建Redux Store的代码,代码如下:
import {createStore, compose} from 'redux';
const reducer = f => f;
const win = window;
const storeEnhancers = compose(
(win && win.devToolsExtension) ? win.devToolsExtension() : (f) => f,
);
const initialState = {};
export default createStore(reducer, initialState, storeEnhancers);
上面定义的Store只是一个例子,并没有添加实际的reducer和初始状态,主要是使用了Redux Devtools。
使用React-Redux库的Provider组件,作为数据的提供者,Provider必须居于接受数据的React组件之上。换句话说,要想让Provider提供的store能够被所有组件访问到,必须让Provider处于组件树结构的顶层,而React-Router库的Router组件,也有同样的需要那么,两者都希望自己处于顶端,如何处理呢?
一种方法是让Router成为Provider的子组件,例如在应用的入口函数src/index.js中代码修改成下面这样:
ReactDOM.render(
document.getElementById('root')
);
Router可以是Provider的子组件,但是,不能够让Provider成为Router的子组件,因为Router的子组件只能是Route或者IndexRoute,否则运行时会报错。
另一个方法,是使用Router的createElement属性,通过给createElement传递一个函数,可以定制创建每个Route的过程,这个函数第一个参数Component代表Route对应的组件,第二个参数代表传入组件的属性参数。
加上Provider的createElement可以这样定义,代码如下:
import store from './Store.js';
const createElement = (Component, props) => {
return (
);
};
const Routes = () => (
...
需要注意的是,Router会对每个Route的构造都调用一遍createElement,也就是每个组件都创造了一个Provider来提供数据,这样并不会产生性能问题,但如果觉得这样过于浪费的话,那就使用第一种方法。
在第3章我们刚刚接触Redux概念的时候就知道,Redux遵从的一个重要原则就是“唯一数据源”,唯一数据源并不是说所有的数据都要存储在一个地方,而是说一个特定数据只存在一个地方,以路由为例,使用React-Router,即使结合了Redux,当前路由的信息也是存储在浏览器的URL上,而不是像其他数据一样存储在Redux的Store上,这样做并不违背“唯一数据源”的原则,获取路由信息的唯一数据源就是当前URL。
不过,如果不是所有应用状态都存在Store上,就会有一个很大的缺点,就是当利用Redux Devtools做调试时,无法重现网页之间的切换,因为当前路由作为应用状态根本没有在Store状态上体现,而Redux Devtools操纵的只有状态。
为了克服这个缺点,我们可以利用react-router-redux库来同步浏览器URL和Redux的状态。显然,这违反了“唯一数据源”的规则,但是只要两者绝对保持同步,就并不会带来问题。而如果两个数据源内容不一致,那就会出大问题。
react-router-redux库的工作原理是在Redux Store的状态树上routing字段中保存当前路由信息,因为修改Redux状态只能通过reducer,所以先要修改src/Store.js中代码,增加routing字段上的规约函数routerReducer:
import {routerReducer} from 'react-router-redux';
const reducer = combineReducers({
routing: routerReducer
});
reducer需要由action对象驱动,我们在src/Routes.js文件中要修改传给Router的history变量,让history能够协同URL和Store上的状态,代码如下:
import {syncHistoryWithStore} from 'react-router-redux';
const history = syncHistoryWithStore(history, store);
react-router-redux库提供的syncHistoryWithStore方法将React-Router提供的browser-History和store关联起来,当浏览器的URL变化的时候,会向store派发action对象,同时监听store的状态变化,当状态树下routing字段发生变化时,反过来会更新浏览器URL。
图11-4 react-router-redux加入的LOCATION_CHANGE类型action
如图11-4所示,在Redux Devtools界面上,每当页面发生切换的时候,可以看到有一个type为@@router/LOCATION_CHANGE的action对象被派发出来,通过跳转到不同的action对象,浏览器的URL和界面也会对应变化。 深入浅出React和Redux