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。

相关文章
相关标签/搜索