這一節主要是講述Redux如何被引入到同構項目中以及其中須要注意的問題。html
從新回顧一下redux的運做流程:react
如今開始建立store。 在項目根目錄的store文件夾(總的store)下:ios
import {createStore, applyMiddleware, combineReducers} from 'redux';
import thunk from 'redux-thunk';
import { reducer as homeReducer } from '../containers/Home/store';
//合併項目組件中store的reducer
const reducer = combineReducers({
home: homeReducer
})
//建立store,並引入中間件thunk進行異步操做的管理
const store = createStore(reducer, applyMiddleware(thunk));
//導出建立的store
export default store
複製代碼
Home文件夾下的工程文件結構以下:redux
//constants.js
export const CHANGE_LIST = 'HOME/CHANGE_LIST';
複製代碼
//actions.js
import axios from 'axios';
import { CHANGE_LIST } from "./constants";
//普通action
const changeList = list => ({
type: CHANGE_LIST,
list
});
//異步操做的action(採用thunk中間件)
export const getHomeList = () => {
return (dispatch) => {
return axios.get('xxx')
.then((res) => {
const list = res.data.data;
console.log(list)
dispatch(changeList(list))
});
};
}
複製代碼
//reducer.js
import { CHANGE_LIST } from "./constants";
const defaultState = {
name: 'sanyuan',
list: []
}
export default (state = defaultState, action) => {
switch(action.type) {
default:
return state;
}
}
複製代碼
//index.js
import reducer from "./reducer";
//這麼作是爲了導出reducer讓全局的store來進行合併
//那麼在全局的store下的index.js中只需引入Home/store而不須要Home/store/reducer.js
//由於腳手架會自動識別文件夾下的index文件
export {reducer}
複製代碼
下面是Home組件的編寫示例。axios
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getHomeList } from './store/actions'
class Home extends Component {
render() {
const { list } = this.props
return list.map(item => <div key={item.id}>{item.title}</div>)
}
}
const mapStateToProps = state => ({
list: state.home.newsList,
})
const mapDispatchToProps = dispatch => ({
getHomeList() {
dispatch(getHomeList());
}
})
//鏈接store
export default connect(mapStateToProps, mapDispatchToProps)(Home);
複製代碼
對於store的鏈接操做,在同構項目中分兩個部分,一個是與客戶端store的鏈接,另外一部分是與服務端store的鏈接。都是經過react-redux中的Provider來傳遞store的。bash
客戶端:服務器
//src/client/index.js
import React from 'react';
import ReactDom from 'react-dom';
import {BrowserRouter, Route} from 'react-router-dom';
import { Provider } from 'react-redux';
import store from '../store'
import routes from '../routes.js'
const App = () => {
return (
<Provider store={store}> <BrowserRouter> {routes} </BrowserRouter> </Provider>
)
}
ReactDom.hydrate(<App />, document.getElementById('root')) 複製代碼
服務端:react-router
//src/server/index.js的內容保持不變
//下面是src/server/utils.js
import Routes from '../Routes'
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import React from 'react'
export const render = (req) => {
const content = renderToString(
<Provider store={store}> <StaticRouter location={req.path} > {Routes} </StaticRouter> </Provider>
);
return ` <html> <head> <title>ssr</title> </head> <body> <div id="root">${content}</div> <script src="/index.js"></script> </body> </html> `
}
複製代碼
其實上面這樣的store建立方式是存在問題的,什麼緣由呢?app
上面的store是一個單例,當這個單例導出去後,全部的用戶用的是同一份store,這是不該該的。那麼這麼解這個問題呢?dom
在全局的store/index.js下修改以下:
//導出部分修改
export default () => {
return createStore(reducer, applyMiddleware(thunk))
}
複製代碼
這樣在客戶端和服務端的js文件引入時其實引入了一個函數,把這個函數執行就會拿到一個新的store,這樣就能保證每一個用戶訪問時都是用的一份新的store。