(06)Header 组件开发——⑥ Immutable.js 助力数据管理 | React.js 项目实战:PC 端“简书”开发

转载请注明出处,未经同意,不可修改文章内容。

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

1 immutable.js 的使用

打开 header 目录下 store 中的 reducer.js 文件:

javascript 复制代码
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;
}

根据之前的 React 基础语法讲解,reducer 在接到"原始 state"后,一定不能对"原始 state"进行任何修改 !reducer 要想返回一个新的"数据"出去,必须要对"原始 state"进行克隆后,才能进行相应地逻辑操作,否者会出现莫名的 bug。

但在实际项目中,由于开发者们的起点不一,我们不能保证每一个开发者都把这一要点牢牢记住并遵从。

幸好的是,Facebook 团队历时 3 年为我们开发出了一个第三方模块------immutable.js,用来确保我们不去修改"原始 state",并提高页面性能。

immutable.js 可以帮我们生成一个"immutable 对象",即"不可变对象"。

所以,只要我们把 "原始 state"变为"immutable 对象","原始 state"就可以不被任何人改变了,reducer 也就不会出莫名的 bug。

1️⃣安装 immutable.js

2️⃣打开 header 目录下 store 中的 reducer.js 文件,将"原始 state"变为"immutable 对象":

javascript 复制代码
import {CHANGE_CLASS_NAME, RESUME_CLASS_NAME} from "./actionTypes";

/*
❗️❗️❗️2️⃣-①:从 immutable 中引入 fromJS 方法,这个"方法"可以
帮我们把一个"JS 对象"转化为一个"immutable 对象";
 */
import {fromJS} from "immutable";

/*
2️⃣-②:先把原来"JS 对象"的写法注释掉~/
const defaultState = {
  refresh: false
}
 */
// 2️⃣-③:用 fromJS 将"JS 对象"转变为"immutable 对象";
const defaultState = fromJS({
  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 目录下的 index.js 文件:

jsx 复制代码
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";

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️⃣-④:由于这里的 state.header 指的就是上一步中 
    reducer 返回的"数据",但这个"数据"已经变成了"immutable 对象"了,
    故直接用 state.header. "改变"数据的语法是不可以的!
    ❗️需要用 .get() 的 immutable 特定方法来获取对应的属性!
    refresh: state.header.refresh
     */
    refresh: state.header.get("refresh")
  }
}

const mapDispatchToProps = (dispatch) => {  
  return {
    handleMouseDown() { 
    
      const action = actionCreators.changeClassNameAction(); 
  
      dispatch(action)
    
    },

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

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

❌经过以上两步,虽然数据获取的流程跑通了,但"数据改变"的流程还未通!

打开 header 目录下 store 中的 reducer.js 文件:

javascript 复制代码
import {CHANGE_CLASS_NAME, RESUME_CLASS_NAME} from "./actionTypes";

import {fromJS} from "immutable";

const defaultState = fromJS({  /*❗️*/
  refresh: false
})

export default (state=defaultState, action) => {
  if(action.type === CHANGE_CLASS_NAME) {
    
    /*
    ❗️2️⃣-⑤:当用户 mousedown 按钮时,会派发一个 action 给到 reducer,
    reducer 给 store 返回一个新的数据。由于上一步中,我们已经让 state 变为了
    immutable 对象,这里就不能再像原来那样返回"普通对象"了!
    ❗️❗️❗️需要利用 immutable 提供的 set() 方法来设置"数据"!
    它的背后原理为:immutable 对象的 set 方法会结合"之前 immutable 的值"和"设置的值",
    返回一个全新的对象(它并没有去改"原始 state")!
    return {
      refresh: true
    }
     */
    return state.set("refresh", true); // 🚀注意 .set() 方法的格式!
    
  }
  
  if(action.type === RESUME_CLASS_NAME) {
    /*
    同理,把这里的代码注释掉,并进行改写~
    return {
      refresh: false
    }
     */
    return state.set("refresh", false);
    
  }
   
  return state;
}

返回页面查看效果:

2 redux-immutable 的使用

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

jsx 复制代码
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";

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 { 
   
    /*
    ❌这行代码中,state 是 src 目录下 store 中 reducer.js 里的"JS 对象",
    而 state.header 又是 header 目录下 store 中 reducer.js 的"immutable 对象"。
    这样"JS 对象"和"immutable 对象"揉在一起获取"数据"的方式很不利于后期维护!
    ❗️故,我们既然用了 immutable,那么我们最好就把所有"数据"统一为"immutable 对象"。
     */
    refresh: state.header.get("refresh")
  }
}

const mapDispatchToProps = (dispatch) => {  
  return {
    handleMouseDown() { 
    
      const action = actionCreators.changeClassNameAction(); 
  
      dispatch(action)
    
    },

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

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

1️⃣安装 redux-immutable

2️⃣打开 src 目录下 store 中 reducer.js 文件:

javascript 复制代码
/*
2️⃣-①:之前,我们的 combineReducers 来自 redux。但有了 redux-immutable 后,
redux-immutable 也为我们提供了一个 combineReducers。
用它生成的"数据"就为"immutable 对象"
import {combineReducers} from "redux";  
 */
import {combineReducers} from "redux-immutable";

import {reducer as headerReducer} from "../common/header/store"; 


const reducer = combineReducers({ // 2️⃣-②:❗️生成的 reducer 是一个"immutable 对象"!
  header: headerReducer
})


export default reducer;

返回 header 目录下的 index.js 文件:

jsx 复制代码
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";

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 { 
   
    /*
    ❌这行代码中,state 是 src 目录下 store 中 reducer.js 里的"JS 对象",
    而 state.header 又是 header 目录下 store 中 reducer.js 的"immutable 对象"。
    这样"JS 对象"和"immutable 对象"揉在一起获取"数据"的方式很不利于后期维护!
    ❗️故,我们既然用了 immutable,那么我们最好就把所有"数据"统一为"immutable 对象"。
     */
    /*
    3️⃣既然上一步已经让 state 成为"immutable 对象",那么这里就可以用其提供的 
    get 方法获取"数据"。由于这里有两层 get,故可以用 immutable 提供的 getIn([]) 方法简写~
    refresh: state.header.get("refresh")
     */
    refresh: state.getIn(["header", "refresh"]) /*
    																		 ❗️它其实是 
                                          refresh: state.get("header").get("refresh")
                                          的简写!  
                                                 */
  }
}

const mapDispatchToProps = (dispatch) => {  
  return {
    handleMouseDown() { 
    
      const action = actionCreators.changeClassNameAction(); 
  
      dispatch(action)
    
    },

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

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

返回页面查看效果:

祝好,qdywxs ♥ you!

相关推荐
吕彬-前端4 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱7 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai16 分钟前
uniapp
前端·javascript·vue.js·uni-app
也无晴也无风雨17 分钟前
在JS中, 0 == [0] 吗
开发语言·javascript
bysking1 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓1 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205872 小时前
web端手机录音
前端