(05)Header 组件开发——⑤ React-redux 搭建标准的项目架子 | React.js 项目实战:PC 端“简书”开发

3,882 阅读12分钟
转载请注明出处,未经同意,不可修改文章内容。

🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。


1 取得并显示“数据”

对于一个正经的实战项目而言,Redux 这种“数据层”的框架是肯定要用的。

而一旦用了 Redux,那我们最好是把所有“数据”都放在它里边去管理。

不要管这个“数据”在是组件“私有”的,还是各组件“共享”的。因为,随着页面的增多,功能变得越来越复杂,你根本没办法保证之前的“私有”数据还依然是“私有”。故,统统都放到 Redux 中进行管理是最佳实践,这也为我们后期代码的维护减轻了很大的负担!

本篇我们再仔细走走 Redux 和 React-redux 的流程,慢一点,本篇将整个项目的架子搭起来,后续的开发就跟着套路用就是了。

1️⃣在项目 qdywxs-jianshu 中分别安装 Redux 和 React-redux: 5-1.png

5-2.png

2️⃣接着,在 src 目录下创建一个“文件夹” store ,并在 store 文件夹下创建一个“文件” index.js ——即,存放 store 代码的位置: 5-3.png

3️⃣有了存放 store 代码的位置 index.js ,我们就可以在里边用代码来创建一个数据的“公共存储仓库”:

import { createStore } from "redux"; /*
																		 3️⃣-①:从 redux 这个第三方模块中,
																		 引入 createStore 方法;
                                      */

const store = createStore(); // 3️⃣-②:调用这个方法创建一个 store;

export default store; // 3️⃣-③:将创建的 store 导出!

4️⃣❗️❗️❗️由于 store 仅仅是一个“图书管理员”,它记不住到底应该怎么去管理数据。故,它需要一个“记录本”reducer 来辅助它管理数据。因此,我们在创建 store 的同时,必须要把这个“记录本”一并传给 store,否则 store 什么也不知道。

4️⃣-①:所以,我们还得在 store 目录下创建 reducer.js 文件; 5-4.png

4️⃣-②:在 Redux 中, reducer.js 文件需要返回一个“函数”;

const defaultState = { /*
											 4️⃣-④:既然 reducer 的一个重要功能是“放置数据”,
											 那么根据之前做 TodoList 的经验,
                       本项目 Header 组件此时有一项“默认数据”:refresh;
                        */
	
  refresh: false // ❗️默认 refresh 的初始值为 false,表示 span 标签没有这个 class 名!

}

// 4️⃣-⑤:上边有了“默认数据”,这里记得让“参数”state 等于 defaultState;
export default (state = defaultState, action) => { /*
																	4️⃣-③:返回的“函数”接受两个固定参数:
																  state 和 action。
                                  state 指:整个仓库里存储的数据(可以形象的理解为,“记录本”
                                  里记录的“图书馆”中所有的书籍信息);
                                  
                                  action 指:传过来的那句话。
                                                    */
  
  return state; // ❗️默认返回 state。
}

4️⃣-⑥:同时,在前边创建 store 的时候index.js ),我们要把“记录本”reducer 传递给 store,并将 reducer 作为第一个“参数”传递给“方法” createStore()

import { createStore } from "redux";

import reducer from "./reducer"; // 🚀从当前目录下的 reducer.js 引入 reducer。

const store = createStore(reducer); /*
																		❗️❗️❗️将 reducer 作为第一个“参数”
																		传递给“方法”createStore!
                                     */

export default store;

4️⃣-⑦:既然把“记录本”reducer 传递给了 store,那么 store 就知道这个仓库里边有 refresh 这个数据了;

5️⃣通过以上的步骤,store 里边就有数据。按照 Redux 和 React-redux 的工作流程,一旦有了数据,各“组件”就可以连接 store,去 store 里边取数据并显示出来!

5️⃣-①:打开 src 目录下的 App.js 文件;

import React, { Component } from "react";

import {GlobalStyle} from "./style";

import {GlobalIconStyle} from "./statics/iconfont/iconfont";

import Header from "./common/header";

/*
❗️❗️❗️5️⃣-②:有了 React-redux 后,我们会首先从 react-redux 引入一个 Provider 组件(
它是 React-redux 的核心 API 之一);
 */
import { Provider } from "react-redux";

// 5️⃣-③:同时,在本文件中引入 store;
import store from "./store";

class App extends Component  {  
  render() {  
    return (
      <div>
        <GlobalStyle />
        <GlobalIconStyle />
      
      	{/* 5️⃣-④:用 Provider 组件包裹“组件”Header; */}
        <Provider store={store}> {/*
        												  ❗️❗️❗️5️⃣-⑤:给 Provider 添加一个属性,
                                  使其等于“5️⃣-③”中引入的 store。
                                  这一步的意思为:
                                  “提供器 Provider”连接了 store,那么 Provider 里边的所有
                                  “组件”(如这里的 Header)都有能力获取到 store 里的数据了!
                                   */}
        	<Header />
        </Provider>
      </div>
    );
  }
}

export default App; 

6️⃣通过上边的操作后,Header 组件已经拥有了获取 store 中数据的“能力”。可光有“能力”可不行,要具体怎么去获取呢?

6️⃣-①:打开 header 目录下的 index.js 文件;

import React, {Component} from "react";

import {
  HeaderWrapper,
  Logo,
  
  Navbar,
  ItemList,
  LinkList,
  
  SearchArea,
  SearchInput,
  SearchPanel,
  PanelTitle,
  PanelChange,
  PanelLabels,
  LabelLink,
  
  Extra,
  ExtraLink
  
} from "./style";

/*
❗️❗️❗️6️⃣-②:从 react-redux 中引入 connect 方法(它也是 React-redux 的核心 API 之一),
connect 的作用很明确——就是“连接”的意思!
 */
import { connect } from "react-redux";

class Header extends Component {
  /*
  ❗️6️⃣-①-1:在上边的“4️⃣-④”中,“数据”已经被定义到了 reducer 中,
  故本文件中删除掉定义“数据”的代码;
  constructor(props) {
    super(props);
    this.state = {
      refresh: false  
    }
    
    ❗️React-redux 的引入,与“交互”相关的代码都不需要这样写了,故删除!
    this.handleMouseDown = this.handleMouseDown.bind(this);
    this.handleMouseUp = this.handleMouseUp.bind(this)
  }
   */

  render() {
    return (
      <HeaderWrapper>
        <Logo>
          <img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
        </Logo>

        <Navbar className="clearfix">
          <ItemList className="active">
            <LinkList href="/">
              首页
            </LinkList>
          </ItemList>

          <ItemList>
            <LinkList href="/">
              下载APP
            </LinkList>           
          </ItemList>
        </Navbar>
      
        <SearchArea>
          <SearchInput />
          <span className="iconfont icon-search">&#xe63e;</span>
      
          <SearchPanel>
            <PanelTitle>
              热门搜索
      
              {/*
               ❗️6️⃣-①-2:先不用写“交互”事件,稍后会按 React-redux 的流程来重写!
              <PanelChange onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
      			    */}
  						<PanelChange>
                
                {/*
                 ❗️❗️❗️6️⃣-⑨:“映射”上了过后,我们就可以通过调用 this.props.refresh 来
                 “调用”store 中的 refresh 了!
                 删除下边的代码,重新用 this.props 来改写~
                <span className={this.state.refresh ? "iconfont refresh" : "iconfont"}>&#xe65f;</span>
                  */}
                <span className={this.props.refresh ? "iconfont refresh" : "iconfont"}>&#xe65f;</span>

                换一批
              </PanelChange>
            </PanelTitle>
      
            <PanelLabels className="clearfix">
              <LabelLink href="/">
                区块链
              </LabelLink>
              <LabelLink href="/">
                故事
              </LabelLink>
              <LabelLink href="/">
                小程序
              </LabelLink>
              <LabelLink href="/">
                前端一万小时
              </LabelLink>
            </PanelLabels>
          </SearchPanel>
        </SearchArea>
      
      
        <Extra>
          <span className="iconfont icon-textsize" >&#xe739;</span>
          <ExtraLink className="login" href="/">
            登录
          </ExtraLink>
          <ExtraLink className="register" href="/">
            注册
          </ExtraLink> 
      
          <ExtraLink className="writing" href="/">
            <span className="iconfont icon-pen">&#xe600;</span>
            写文章
          </ExtraLink>     
        </Extra>
      </HeaderWrapper>
    )
  }
  
  /*
  ❗️6️⃣-①-3:React-redux 的引入,与“交互”相关的代码都不需要这样写了,故删除,稍后重写!
  handleMouseDown() {
    this.setState({
      refresh: true  
    })
  }

  handleMouseUp() {
    this.setState({
      refresh: false 
    })
  }
   */
  
}

// 6️⃣-⑥:接下来,我们先定义“连接”的“规则”;
const mapStateToProps = (state) => { /*
																		 6️⃣-⑦:把 store 里的“数据 state”作为“参数”
																		 传递给 mapStateToProps;
                                      */
  
  return { // ❗️这个“规则”会返回一个“对象”出去!
    refresh: state.refresh /*
    											 ❗️❗️❗️6️⃣-⑧:“规则”的具体做法为——将 store 里的 refresh 
                           映射到“Header 组件”里的 props 的 refresh 中去;
                            */
  
  }

}


/*
❗️❗️❗️6️⃣-③:之前我们直接导出的是 Header,可用了 React-redux 后,就不能这样写了!
export default Header;
 */

/*
6️⃣-④:取而代之,我们是导出 connect 方法(
❗️注意看我们给 connect 方法传递了哪些参数!);
 */
export default connect(mapStateToProps, null)(Header); /*
																				6️⃣-⑤:我们一共要给 connect 传递 3 个参数!
                                        Header 表示:connect 会让“Header 组件”和 store
                                        进行“连接”(由“5️⃣-⑤”可知,Header 已经拥有“能力”
                                        连接 store);
                                        
                                        mapStateToProps 表示:“Header 组件”和 store 
                                        进行“连接”是需要“规则”的,而具体的“规则”就在这个
                                        mapStateToProps 里边(❗️直译为:把 store 里边的
                                        “数据 state”映射到“Header 组件”的 props 里);
                                        
                                        null 表示:这里还会接收一个名叫 
                                        mapDispatchToProps 的参数,等下“改变数据”时讲解,
                                        这里先用 null 占位。
                                                        */

返回页面查看效果(为了测试是否取得了“数据”,可以将 reducer.js 中的 refresh 初始值改为 true ,让  span 标签一开始就有“旋转动画效果”): jianshu_05-05.gif

通过上边的操作,Header 组件就可以获得并显示“数据”了。但,“旋转动画”是一个交互(涉及了用户的操作 action)的过程,只有“鼠标按压下去”的时候,“旋转动画”才会生效(“数据 refresh ”从 false 变成 true )。故,我们需要来走走 React -redux 中“改变数据”的流程!

2 配置 redux devtools

7️⃣🚀既然涉及到“改变数据”,我们就应该先来配置好 redux devtools: 5-6.png

打开 instructions 后,由于本项目后续肯定会用到“中间件”,故配置的时候要将“中间件”考虑进来(❗️但此时不用配置,后边用到“中间件”时再配置此项): 5-7.png

将其粘贴至 src 目录下 store 中的 index.js 文件中:

// 7️⃣-④:将 compose 函数从 redux 中引入进来;
import { createStore, applyMiddleware, compose } from "redux";  

import reducer from "./reducer"; 

// 7️⃣-①:直接拷贝官方文档里的代码;
const composeEnhancers =
  /*
  ❗️7️⃣-②:这行代码可以注释掉,因为浏览器的应用,故 window 的 object 是肯定存在的!
  typeof window === 'object' && 
   */
  
  /*
  ❗️7️⃣-③:下面这行代码的意思为:
  如果 window 下边有 __REDUX_DEVTOOLS_EXTENSION__ 这个变量的话,
  就执行这个变量对应的方法 window.__REDUX_DEVTOOLS_EXTENSION__()。
  否则,
  就执行 compose 函数;
   */
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;


// 7️⃣-⑤:继续拷贝官网的代码;
const enhancer = composeEnhancers( /*
																	 7️⃣-⑥:表示将 composeEnhancers 执行后的
                                   结果赋值给 enhancer;
                                    */
  /*
  ❗️我们暂时不配置“中间件”~
  applyMiddleware(...middleware)
   */
);

const store = createStore(
  reducer,
 
  enhancer // 7️⃣-⑦:直接将 enhancer 传递进来即可!
  
  
);  

export default store; 

再次在页面查看(很多数据信息都一目了然了,接下来再做更复杂的 Redux 调试就很方便了): jianshu_05-08.gif

3 改变“数据”

8️⃣本项目中,我要做的事是“改变 span 标签的 class 名”来控制执行“旋转动画”与否。

在编写 header 目录下的 index.js 文件之前,我们得按照实际项目的开发流程,先拆分 actionTypesactionCreators5-9.png

actionTypes.js 文件:

export const CHANGE_CLASS_NAME = "change_class_name";
export const RESUME_CLASS_NAME ="resume_class_name";

actionCreators.js 文件:

import {CHANGE_CLASS_NAME, RESUME_CLASS_NAME} from "./actionTypes";

export const changeClassNameAction = () => ({
  type: CHANGE_CLASS_NAME
})

export const resumeClassNameAction = () => ({
  type: RESUME_CLASS_NAME
})

9️⃣打开 header 目录下的 index.js 文件:

import React, {Component} from "react";

import {
  HeaderWrapper,
  Logo,
  
  Navbar,
  ItemList,
  LinkList,
  
  SearchArea,
  SearchInput,
  SearchPanel,
  PanelTitle,
  PanelChange,
  PanelLabels,
  LabelLink,
  
  Extra,
  ExtraLink
  
} from "./style";

import { connect } from "react-redux";

// ❗️❗️❗️从 src 目录下的 store 中引入 actionCreators 中创建好的“方法”!
import {changeClassNameAction, resumeClassNameAction} from "../../store/actionCreators";

class Header extends Component {
  render() {
    return (
      <HeaderWrapper>
        <Logo>
          <img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
        </Logo>

        <Navbar className="clearfix">
          <ItemList className="active">
            <LinkList href="/">
              首页
            </LinkList>
          </ItemList>

          <ItemList>
            <LinkList href="/">
              下载APP
            </LinkList>           
          </ItemList>
        </Navbar>
      
        <SearchArea>
          <SearchInput />
          <span className="iconfont icon-search">&#xe63e;</span>
      
          <SearchPanel>
            <PanelTitle>
              热门搜索
      
              <PanelChange
                onMouseDown={this.props.handleMouseDown}
                onMouseUp={this.props.handleMouseUp}
              > {/*
              	 9️⃣-④:给 PanelChange 样式组件绑定一个“事件 onMouseDown”,
                 ❓可这个“事件”应该怎样被调用呢?
                  */}
                {/*
                 9️⃣-⑥:因此可以通过 this.props.handleMouseDown 和 
                 this.props.handleMouseUp 来分别调用 store 的 handleMouseDown 和
                 handleMouseUp;
                  */}
                  
                <span className={this.props.refresh ? "iconfont refresh" : "iconfont"}>&#xe65f;</span>

                换一批
              </PanelChange>
            </PanelTitle>
      
            <PanelLabels className="clearfix">
              <LabelLink href="/">
                区块链
              </LabelLink>
              <LabelLink href="/">
                故事
              </LabelLink>
              <LabelLink href="/">
                小程序
              </LabelLink>
              <LabelLink href="/">
                前端一万小时
              </LabelLink>
            </PanelLabels>
          </SearchPanel>
        </SearchArea>
      
      
        <Extra>
          <span className="iconfont icon-textsize" >&#xe739;</span>
          <ExtraLink className="login" href="/">
            登录
          </ExtraLink>
          <ExtraLink className="register" href="/">
            注册
          </ExtraLink> 
      
          <ExtraLink className="writing" href="/">
            <span className="iconfont icon-pen">&#xe600;</span>
            写文章
          </ExtraLink>     
        </Extra>
      </HeaderWrapper>
    )
  }

}

const mapStateToProps = (state) => { 
  return { 
    refresh: state.refresh  
  }
}

/*
❗️❗️❗️9️⃣-②:接下来,我们定义哪些“用户的操作”
应该当作 action,并传给 store;
 */
const mapDispatchToProps = (dispatch) => { /*
																					 9️⃣-③:把 store 里的“dispatch 方法”
                                           作为“参数”传递给 mapDispatchToProps;
                                            */
  return {
    handleMouseDown() { /*
    										9️⃣-⑤:在这里定义用户的“onMouseDown 操作”会被当作 action
                        传给 store;
                         */
    
      const action = changeClassNameAction();
  
      dispatch(action)
    
    },

    handleMouseUp() {
      const action = resumeClassNameAction();
      dispatch(action)
    }
  }
}

/*
❗️9️⃣-①:给 connect 传递第 3 个参数——mapDispatchToProps。
mapDispatchToPropos 直译为:我们把 store 的 dispatch 方法“挂载”到
Header 组件的 props 上。
即,我们可以定义哪些“用户的操作”应该当作 action,并传给 store!
 */
export default connect(mapStateToProps, mapDispatchToProps)(Header); /*
																																		 ❗️导出一个
                                                                     “容器组件”!
                                                                      */

9️⃣-⑦:打开 src  目录中 store 下的 reducer.js 文件:

// 9️⃣-⑧:引入“常量”;
import {CHANGE_CLASS_NAME, RESUME_CLASS_NAME} from "./actionTypes";

const defaultState = {
  refresh: false
}

export default (state=defaultState, action) => {
  if(action.type === CHANGE_CLASS_NAME) {
    return {
      refresh: true
    }
  }
  
  if(action.type === RESUME_CLASS_NAME) {
    return {
      refresh: false
    }
  }
   
  return state;
}

返回页面查看效果(一切正常运行): jianshu_05-10.gif

4 combineReducers 对数据进行拆分管理

理论上说,上文的一系列操作已成功实现了效果。但,其中依然有一些非最佳实践的操作。

上文中,我们把所有数据都放在 src 目录下 store 文件夹里的 reducer.js 中进行维护。在“数据”和“处理逻辑”较少的情况下没什么问题,但随着页面的增多,项目更加复杂,“数据”量会越来越大,“处理逻辑”相关的代码也会占据 reducer.js 文件很大的篇幅!

在实际工作中,我们一般不要在同一个文件中写超过 300 行的代码,不然后期维护起来将变得特别棘手!

故,本项目中我们要想办法不要让 reducer.js 文件变得那么大。

🚀幸运的是,Redux 为我们提供了一个很好的思路和方法——combineReducers(形象地解释为:“图书管理员”在查询“小册子”时,这个小册子不再是厚厚的一大本,而是分门别类的一小本。“管理员”就可以很容易地在分类下找到需要的书籍~)。

🔟-①:打开 src 目录 common 下的文件夹 header ,header 下有很多只属于 Header 组件的“数据”,我们就可以将这些“数据”放在 header 里进行管理。 在 header 目录下新建一个文件夹 store5-11.png

🔟-②:紧接着,我们在 store 文件夹下创建一个 index.js 文件,它的作用为——❗️它是这整个 store 的“出口”文件(“出口”文件的好处为:它可以简化引用文件时的“路径”,也便于后期维护~)5-12.png

🔟-③:在 store 文件夹下创建 reducer.js 文件; 5-13.png

🔟-④:同理,在 store 文件夹下创建 actionTypes.jsactionCreators.js 文件; 5-14.png

架子搭起后,我们来挨个修改各文件里的代码:

🔟-⑤:将原本 src 目录下 store 文件夹里的 actionTypes.jsactionCreators 这两个文件中的代码移至 header 目录下 store 文件夹中,并同时删除 src 目录下 store 文件夹里的 actionTypes.js 和 actionCreators 这两个文件。 5-15.png

🔟-⑥:将原本 src 目录下 store 文件夹里 reducer.js 文件中的代码“剪切”至 header 目录下 store 中的 reducer.js 中;

import {CHANGE_CLASS_NAME, RESUME_CLASS_NAME} from "./actionTypes";

const defaultState = {
  refresh: false
}

export default (state=defaultState, action) => {
  if(action.type === CHANGE_CLASS_NAME) {
    return {
      refresh: true
    }
  }
  
  if(action.type === RESUME_CLASS_NAME) {
    return {
      refresh: false
    }
  }
   
  return state;
}

🔟-⑦:❗️打开 header 目录下 store 中的 index.js 这个 store 的“出口”文件,我们将需要传递出去的东西在里边定义好;

import reducer from "./reducer";
import * as actionTypes from "./actionTypes";
import * as actionCreators from "./actionCreators";

export {reducer, actionTypes, actionCreators};

header 的 store 下各文件都写好了,并有了自己的“出口”文件,我们就需要去修改其他文件中的引用“路径”: 🔟-⑧:打开 header 目录下的 index.js 文件;

import React, {Component} from "react";

import {
  HeaderWrapper,
  Logo,
  
  Navbar,
  ItemList,
  LinkList,
  
  SearchArea,
  SearchInput,
  SearchPanel,
  PanelTitle,
  PanelChange,
  PanelLabels,
  LabelLink,
  
  Extra,
  ExtraLink
  
} from "./style";

import { connect } from "react-redux";

/*
🔟-⑧-1:简化这里的引用!
import {changeClassNameAction, resumeClassNameAction} from "../../store/actionCreators";
 */
import {actionCreators} from "./store";


class Header extends Component {
  render() {
    return (
      <HeaderWrapper>
        <Logo>
          <img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
        </Logo>

        <Navbar className="clearfix">
          <ItemList className="active">
            <LinkList href="/">
              首页
            </LinkList>
          </ItemList>

          <ItemList>
            <LinkList href="/">
              下载APP
            </LinkList>           
          </ItemList>
        </Navbar>
      
        <SearchArea>
          <SearchInput />
          <span className="iconfont icon-search">&#xe63e;</span>
      
          <SearchPanel>
            <PanelTitle>
              热门搜索
      
              <PanelChange
                onMouseDown={this.props.handleMouseDown}
                onMouseUp={this.props.handleMouseUp}
              > 
                <span className={this.props.refresh ? "iconfont refresh" : "iconfont"}>&#xe65f;</span>

                换一批
              </PanelChange>
            </PanelTitle>
      
            <PanelLabels className="clearfix">
              <LabelLink href="/">
                区块链
              </LabelLink>
              <LabelLink href="/">
                故事
              </LabelLink>
              <LabelLink href="/">
                小程序
              </LabelLink>
              <LabelLink href="/">
                前端一万小时
              </LabelLink>
            </PanelLabels>
          </SearchPanel>
        </SearchArea>
      
      
        <Extra>
          <span className="iconfont icon-textsize" >&#xe739;</span>
          <ExtraLink className="login" href="/">
            登录
          </ExtraLink>
          <ExtraLink className="register" href="/">
            注册
          </ExtraLink> 
      
          <ExtraLink className="writing" href="/">
            <span className="iconfont icon-pen">&#xe600;</span>
            写文章
          </ExtraLink>     
        </Extra>
      </HeaderWrapper>
    )
  }

}

const mapStateToProps = (state) => { 
  return { 
    
    /*
    ❗️❗️❗️🔟-⑧-2:既然“数据”已经放到了自己的 Header 组件里,这里“映射”的时候就需要多加一层!
    refresh: state.refresh  
     */
    refresh: state.header.refresh
  }
}

const mapDispatchToProps = (dispatch) => {  
  return {
    handleMouseDown() { 
    
      const action = actionCreators.changeClassNameAction(); /*
      																				 🔟-⑧-3:这里需要在 
      																				 changeClassNameAction 前边加上
                                               actionCreators;
                                                              */
  
      dispatch(action)
    
    },

    handleMouseUp() {
      const action = actionCreators.resumeClassNameAction(); /*
      																											 🔟-⑧-4:同理,
      																											 加上前缀;
                                                              */
      dispatch(action)
    }
  }
}

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

🔟-⑨:❗️❗️❗️“路径”都改好了过后,仅表示你的这个小分类目录跑通了,但“小分类”必须依托于“大分类”才起作用。即,需要将各“组件”的“小 reducer”合并到项目的“大 reducer”中! 打开 src 目录下 store 文件夹里 reducer.js 文件,里边之前的代码已经被剪切,我们来编写新的代码:

import {combineReducers} from "redux"; /*
																			 🔟-⑨-2:从 redux 中引入一个名为 
																			 combineReducers 的函数,其用于“合并”那些
                                       小的 reducer;
                                        */

import {reducer as headerReducer} from "../common/header/store"; /*
																													🔟-⑨-1:先将小的 reducer
                                                          拿到大的 reducer 中;
                                                                  */

// ❓🔟-⑨-3:怎么“合并”呢?
const reducer = combineReducers({ // 🔟-⑨-4:直接调用这个“函数”,传入小的 reducer;
  header: headerReducer
})

// 🔟-⑨-4:最后,将这个“合并”好的 reducer 导出!
export default reducer;

返回页面查看效果: jianshu_05-16.gif

OK,本篇篇幅较长,但可以说是非常重要的一篇,请一定花时间去多写几次!

祝好,qdywxs ♥ you!