likes
comments
collection
share

(08)Header 组件开发——⑧ 热门搜索“换一换”功能实现 | React.js 项目实战:PC 端“简书”开发

作者站长头像
站长
· 阅读数 6
转载请注明出处,未经同意,不可修改文章内容。

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

1 需求

紧接上一篇的代码,在“简书”官网效果中,“热门搜索”区域的“关键字”(AJAX 获取到的“数据”)每页只显示 10  项: (08)Header 组件开发——⑧ 热门搜索“换一换”功能实现 | React.js 项目实战:PC 端“简书”开发

截止目前,我们项目的效果是将所有的“数据”全部都循环出来了,这样肯定是不行的。

2 让每一页仅放置 10 个“关键字”

1️⃣打开 header 目录下 store 中的 reducder.js 文件,我们增加两个初始的“数据项” page: 1totalPage: 1

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

import {fromJS} from "immutable";

const defaultState = fromJS({  /*❗️*/
  refresh: false,
  list: [],
  
  // ❗️增加两个“数据项”~
  page: 1,
  totalPage: 1
})

export default (state=defaultState, action) => {
  if(action.type === CHANGE_CLASS_NAME) {
    return state.set("refresh", true); 
  }
  
  if(action.type === RESUME_CLASS_NAME) {
    return state.set("refresh", false); 
  }
  
  if(action.type === CHANGE_LIST) {
    return state.set("list", action.data)
  }
   
  return state;
}

2️⃣然后,当“聚焦 onfocus” SearchInput 样式组件时,会去获取 AJAX 数据。“数据”获取到后,会执行 changeListAction 这个 action。

2️⃣-①:我们打开 header 目录下 store 中的 actionCreators.js 文件;

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


import axios from "axios";

import {fromJS} from "immutable";

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

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

const changeListAction = (data) => ({
  type: CHANGE_LIST,
  data: fromJS(data),
  
  // ❗️❗️❗️2️⃣-②:我们同时返回一个“总页码”出去;
  totalPage: Math.ceil(data.length / 10) // ❗️注意我们是怎样得出这个“总页码”的!
})

export const initLabelAction = () => {
  return(dispatch) => {
    axios.get("/api/headerList.json")
      .then((res) => {
        const data = res.data
        
        const action = changeListAction(data.data);
        dispatch(action)
      })
      .catch(() => {alert("error")})
  }
}

3️⃣接着,action 会传递给 reducer。

3️⃣-①:打开 header 目录下 store 中的 reducer.js 文件;

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

import {fromJS} from "immutable";

const defaultState = fromJS({  
  refresh: false,
  list: [],
  
  page: 1,
  totalPage: 1
})

export default (state=defaultState, action) => {
  if(action.type === CHANGE_CLASS_NAME) {
    return state.set("refresh", true); 
  }
  
  if(action.type === RESUME_CLASS_NAME) {
    return state.set("refresh", false); 
  }
  
  if(action.type === CHANGE_LIST) {
    
    /*
    ❗️3️⃣-②:返回给 store 的数据还应包括修改后的“总页码”~
    return state.set("list", action.data)
     */
    return state.set("list", action.data).set("totalPage", action.totalPage)
  }
   
  return state;
}

4️⃣接下来,Header 组件就可以去获取到这两个“数据项”了。

4️⃣-①:打开 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";

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
            onFocus={() => this.props.handleInputFocus(this.props.list)}
          />
      
          <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">
              {
                this.props.list.map((item) => {
                  return <LabelLink key={item} href="/">{item}</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.getIn(["header", "refresh"]),
    list: state.getIn(["header", "list"]),
    
    // ❗️❗️❗️4️⃣-②:获取到新增的那两个“数据项”;
    page: state.getIn(["header", "page"]),
    totalPage: state.getIn(["header", "totalPage"])
  }
}

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

    handleMouseUp() {
      const action = actionCreators.resumeClassNameAction();
      dispatch(action)
    },
    
    
    handleInputFocus(list) {  
      if(list.size === 0) { 
        const action = actionCreators.initLabelAction();
        dispatch(action)
      }
    }
  }
}

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

5️⃣组件获取到数据后,我们就可以来利用这些“数据”进行“需求”的实现了——让每一页仅放置 10 个“关键字”。

5️⃣-①:返回 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";

import {actionCreators} from "./store";


class Header extends Component {
  
  // 5️⃣-③:在 render 函数的上边写 getPanels 方法;
  getPanels() {
    
    const newList = this.props.list.toJS(); /*
    																				5️⃣-⑦:由于 list 是“immutable 对象”,
                                            “immutable 对象”是没办法通过 list[] 的形式
                                            获取到值的。故,我们要将“immutable 对象”转化
                                            为“JS 对象”;
                                             */
    
    const pageLabels = []; // 5️⃣-⑤:新建一个空数组;
    
    // 5️⃣-④:首先,我们可以通过 page 来获取到每一页内容的“索引”,然后进行 for 循环;
    for(let i=(this.props.page - 1)*10; i<this.props.page*10; i++) {  
      
      pageLabels.push( // 5️⃣-⑨:将循环出的每一项都增加到数组 pageLabels 中;
        
        // 5️⃣-⑥:循环将 list 对应“索引 i”的内容渲染到“样式组件 LabelLink”中;
        <LabelLink key={newList[i]} href="/">  
          {newList[i]} {/* 5️⃣-⑧:“JS 对象”就可以通过 newList[i] 取到具体索引的值了; */}
        </LabelLink>
      )
    }
    return pageLabels; // ❗️❗️❗️5️⃣-⑩:请一定记得让函数返回一个值!
  }  

  
  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
            onFocus={() => this.props.handleInputFocus(this.props.list)}
          />
      
          <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">
  						
              {/* ❗️5️⃣-②:由于这里的逻辑代码很多,所以我们专门写成一个函数来调用; */}
              {this.getPanels()}

            </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.getIn(["header", "refresh"]),
    list: state.getIn(["header", "list"]),
    
    page: state.getIn(["header", "page"]),
    totalPage: state.getIn(["header", "totalPage"])
  }
}

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

    handleMouseUp() {
      const action = actionCreators.resumeClassNameAction();
      dispatch(action)
    },
    
    
    handleInputFocus(list) {  
      if(list.size === 0) { 
        const action = actionCreators.initLabelAction();
        dispatch(action)
      }
    }
  }
}

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

返回页面查看效果: (08)Header 组件开发——⑧ 热门搜索“换一换”功能实现 | React.js 项目实战:PC 端“简书”开发

3 点击“换一换”切换页面

6️⃣给 PanelChange 样式组件增加一个 onclick 事件。

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";

import { connect } from "react-redux";

import {actionCreators} from "./store";


class Header extends Component {
  
  getPanels() {
    const newList = this.props.list.toJS();
    const pageLabels = [];  
    
    for(let i=(this.props.page - 1)*10; i<this.props.page*10; i++) {  
      pageLabels.push(  
        <LabelLink key={newList[i]} href="/">  
          {newList[i]} 
        </LabelLink>
      )
    }
    return pageLabels; 
  } 
  
  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
            onFocus={() => this.props.handleInputFocus(this.props.list)}
          />
      
          <span className="iconfont icon-search">&#xe63e;</span>
      
          <SearchPanel>
            <PanelTitle>
              热门搜索
      
              <PanelChange
                onMouseDown={this.props.handleMouseDown}
                onMouseUp={this.props.handleMouseUp}

                onClick={() => this.props.handleChangePage(this.props.page, this.props.totalPage)}
              > {/*
              	 ❗️❗️❗️6️⃣-②:绑定一个 onclick 事件。并用“箭头函数”的形式传递出“
                 当前 page”和“totalPage”;
                  */}
                  
                <span className={this.props.refresh ? "iconfont refresh" : "iconfont"}>&#xe65f;</span>
                换一批
              </PanelChange>
            </PanelTitle>
      
            <PanelLabels className="clearfix">
              {this.getPanels()}
            </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.getIn(["header", "refresh"]),
    list: state.getIn(["header", "list"]),
    
    page: state.getIn(["header", "page"]),
    totalPage: state.getIn(["header", "totalPage"])
  }
}

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

    handleMouseUp() {
      const action = actionCreators.resumeClassNameAction();
      dispatch(action)
    },
    
    
    handleInputFocus(list) {  
      if(list.size === 0) { 
        const action = actionCreators.initLabelAction();
        dispatch(action)
      }
    },
    
    // 6️⃣-③:当用户“点击”时,向 store 发送相应满足条件的 action;
    handleChangePage(page, totalPage) {
      if(page < totalPage) {
        dispatch(actionCreators.changePageAction(page + 1))
      }else {
        dispatch(actionCreators.changePageAction(1))
      }
    }
  }
}

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

7️⃣到 header 目录下 store 中的 actionTypes.js 文件中定义“方法”的“类型”。

7️⃣-①:打开 header 目录下 store 中的 actionTypes.js 文件;

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

export const CHANGE_LIST="change_list";  

export const CHANGE_PAGE="change_page"; // ❗️定义好常量!

7️⃣-②:打开 header 目录下 store 中的 actionCreators.js 文件,定义 action;

// 7️⃣-③:引入“常量”;
import {CHANGE_CLASS_NAME, RESUME_CLASS_NAME, CHANGE_LIST, CHANGE_PAGE} from "./actionTypes";

import axios from "axios";

import {fromJS} from "immutable";

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

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

const changeListAction = (data) => ({
  type: CHANGE_LIST,
  data: fromJS(data),
  
  totalPage: Math.ceil(data.length / 10)  
})

export const initLabelAction = () => {
  return(dispatch) => {
    axios.get("/api/headerList.json")
      .then((res) => {
        const data = res.data
        
        const action = changeListAction(data.data);
        dispatch(action)
      })
      .catch(() => {alert("error")})
  }
}


// 7️⃣-④:定义 action;
export const changePageAction = (page) => ({
  type: CHANGE_PAGE,
  page: page
})

7️⃣-⑤:打开 header 目录下 store 中的 reducer.js 文件,传递 action;

// 7️⃣-⑥:引入“常量”;
import {CHANGE_CLASS_NAME, RESUME_CLASS_NAME, CHANGE_LIST, CHANGE_PAGE} from "./actionTypes";

import {fromJS} from "immutable";

const defaultState = fromJS({  
  refresh: false,
  list: [],
  
  page: 1,
  totalPage: 1
})

export default (state=defaultState, action) => {
  if(action.type === CHANGE_CLASS_NAME) {
    return state.set("refresh", true); 
  }
  
  if(action.type === RESUME_CLASS_NAME) {
    return state.set("refresh", false); 
  }
  
  if(action.type === CHANGE_LIST) {
    return state.set("list", action.data).set("totalPage", action.totalPage)
  }
  
  // 7️⃣-⑦:编写返回的“数据”;
  if(action.type === CHANGE_PAGE) {
    return state.set("page", action.page);
  }
   
  return state;
}

返回页面查看效果(除了 key 值报警告外,一切正常): (08)Header 组件开发——⑧ 热门搜索“换一换”功能实现 | React.js 项目实战:PC 端“简书”开发

4 解决 key 值报警告的 bug

以上视频效果中,我们清晰地发现,当页面刷新时,key 值就报警告了(即,一开始就报了警告)。

8️⃣回到 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";

import {actionCreators} from "./store";


class Header extends Component {
  
  getPanels() {
    const newList = this.props.list.toJS();
    const pageLabels = [];  
    
    for(let i=(this.props.page - 1)*10; i<this.props.page*10; i++) { 
      
      // 8️⃣-①:❗️我们在控制台打印一下这里的 newList[i] 是什么东西;
      console.log(newList[i])
      
      pageLabels.push(  
        <LabelLink key={newList[i]} href="/">  
          {newList[i]} 
        </LabelLink>
      )
    }
    return pageLabels; 
  } 
  
  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
            onFocus={() => this.props.handleInputFocus(this.props.list)}
          />
      
          <span className="iconfont icon-search">&#xe63e;</span>
      
          <SearchPanel>
            <PanelTitle>
              热门搜索
      
              <PanelChange
                onMouseDown={this.props.handleMouseDown}
                onMouseUp={this.props.handleMouseUp}

                onClick={() => this.props.handleChangePage(this.props.page, this.props.totalPage)}
              >  
                  
                <span className={this.props.refresh ? "iconfont refresh" : "iconfont"}>&#xe65f;</span>
                换一批
              </PanelChange>
            </PanelTitle>
      
            <PanelLabels className="clearfix">
              {this.getPanels()}
            </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.getIn(["header", "refresh"]),
    list: state.getIn(["header", "list"]),
    
    page: state.getIn(["header", "page"]),
    totalPage: state.getIn(["header", "totalPage"])
  }
}

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

    handleMouseUp() {
      const action = actionCreators.resumeClassNameAction();
      dispatch(action)
    },
    
    
    handleInputFocus(list) {  
      if(list.size === 0) { 
        const action = actionCreators.initLabelAction();
        dispatch(action)
      }
    },
    
    handleChangePage(page, totalPage) {
      if(page < totalPage) {
        dispatch(actionCreators.changePageAction(page + 1))
      }else {
        dispatch(actionCreators.changePageAction(1))
      }
    }
  }
}

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

返回控制台查看: (08)Header 组件开发——⑧ 热门搜索“换一换”功能实现 | React.js 项目实战:PC 端“简书”开发

发现没有?当我们还没请求 AJAX 数据时,for 循环里边的代码也是执行了的。而循环一旦执行,里边也就有了 key 值(即使 key={newList[i]}undefined ),key 值都为 undefined 肯定就会报警告。

❓怎么解这个 bug 呢?

很简单,我们规定只有当 newList 有“数据”后,才执行 for 循环即可!

8️⃣-②:返回 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";

import {actionCreators} from "./store";


class Header extends Component {
  
  getPanels() {
    const newList = this.props.list.toJS();
    const pageLabels = [];  
    
    if(newList.length) { /*
    										 ❗️❗️❗️8️⃣-③:设置一个“条件”,只有 newList 中有“数据”时(
                         即,AJAX 获取到数据后)才执行 for 循环!
                          */
      for(let i=(this.props.page - 1)*10; i<this.props.page*10; i++) { 
        pageLabels.push(  
          <LabelLink key={newList[i]} href="/">  
            {newList[i]} 
          </LabelLink>
        )
      }
      return pageLabels; 
		}
  } 
  
  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
            onFocus={() => this.props.handleInputFocus(this.props.list)}
          />
      
          <span className="iconfont icon-search">&#xe63e;</span>
      
          <SearchPanel>
            <PanelTitle>
              热门搜索
      
              <PanelChange
                onMouseDown={this.props.handleMouseDown}
                onMouseUp={this.props.handleMouseUp}

                onClick={() => this.props.handleChangePage(this.props.page, this.props.totalPage)}
              >  
                  
                <span className={this.props.refresh ? "iconfont refresh" : "iconfont"}>&#xe65f;</span>
                换一批
              </PanelChange>
            </PanelTitle>
      
            <PanelLabels className="clearfix">
              {this.getPanels()}
            </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.getIn(["header", "refresh"]),
    list: state.getIn(["header", "list"]),
    
    page: state.getIn(["header", "page"]),
    totalPage: state.getIn(["header", "totalPage"])
  }
}

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

    handleMouseUp() {
      const action = actionCreators.resumeClassNameAction();
      dispatch(action)
    },
    
    
    handleInputFocus(list) {  
      if(list.size === 0) { 
        const action = actionCreators.initLabelAction();
        dispatch(action)
      }
    },
    
    handleChangePage(page, totalPage) {
      if(page < totalPage) {
        dispatch(actionCreators.changePageAction(page + 1))
      }else {
        dispatch(actionCreators.changePageAction(1))
      }
    }
  }
}

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

返回页面查看效果(一切正常显示): (08)Header 组件开发——⑧ 热门搜索“换一换”功能实现 | React.js 项目实战:PC 端“简书”开发

OK,随着本篇结束,我们的 Header 组件就成功地完成了,讲的比较细,文字也比较多,但都是重点!彻底把 Header 组件掌握后,后边的组件都是一样的套路,没有太多难度。

当然,代码还有一些优化的小地方,但不多(比如,可以用 ES6 的结构赋值减少很多 this.props.xxx 的书写)。我为了讲解清楚思路,故都写的比较细。大伙可以自行稍微优化一下,我们之前的文章都讲过。

下一篇,我们开始“首页”的开发,大家一定理清思路,多敲几次代码!

祝好,qdywxs ♥ you!