React服務端渲染探祕:3.同構項目中引入Redux

這一節主要是講述Redux如何被引入到同構項目中以及其中須要注意的問題。html

從新回顧一下redux的運做流程:react

再回顧一下同構的概念,即在React代碼客戶端和服務器端各自運行一遍。

1、建立全局store

如今開始建立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
複製代碼

2、組件內action和reducer的構建

Home文件夾下的工程文件結構以下:redux

在Home的store目錄下的各個文件代碼示例:

//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}
複製代碼

3、組件鏈接全局store

下面是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> `
}
複製代碼

4、潛在的坑

其實上面這樣的store建立方式是存在問題的,什麼緣由呢?app

上面的store是一個單例,當這個單例導出去後,全部的用戶用的是同一份store,這是不該該的。那麼這麼解這個問題呢?dom

在全局的store/index.js下修改以下:

//導出部分修改
export default  () => {
  return createStore(reducer, applyMiddleware(thunk))
}
複製代碼

這樣在客戶端和服務端的js文件引入時其實引入了一個函數,把這個函數執行就會拿到一個新的store,這樣就能保證每一個用戶訪問時都是用的一份新的store。

相關文章
相關標籤/搜索