我们热门搜索,有一个“换一批”的按钮,点击它,就会换一批热门关键字。
我们的实现是,ajax 获取很多热门搜索关键字,但只会显示十个,点击换一批会显示下一批十个关键字。
那我们再去src/common/header/store 下的reducer.js 中新增两个数据page(当前页)、totalPage(总页数),如下。
import { SEARCH_FOCUS, SEARCH_BLUR, CHANGE_LIST } from './actionTypes';
import { fromJS } from 'immutable';
const defaultState = fromJS({
focused: false,
list: [],
page: 1,
totalPage: 1
});
export default (state = defaultState, action) => {
switch (action.type) {
case SEARCH_FOCUS :
return state.set("focused", true);
case SEARCH_BLUR:
return state.set("focused", false);
case CHANGE_LIST:
return state.set("list", action.data);
default:
return state;
}
}
那我们在返回数据的时候,要修改一下totalPage 的值。因此,我们打开src/common/header/store 下的 actionCreators.js 文件。修改其中的 changeList 方法,如下。
import * as actionTypes from './actionTypes';
import axios from 'axios';
import { fromJS } from 'immutable';
import '../../../mockdata.js';
const changeList = (data) => ({
type: actionTypes.CHANGE_LIST,
data: fromJS(data),
totalPage: Math.ceil( data.length / 10 )
});
export const searchFocus = () => ({
type: actionTypes.SEARCH_FOCUS
});
export const searchBlur = () => ({
type: actionTypes.SEARCH_BLUR
});
export const getList = () => {
return (dispatch) => {
axios.get('/api/headerList.json').then( (res) => {
const data = res.data;
if( data.success ) {
dispatch( changeList( data.data ) )
}
}).catch( () => {
console.log("error");
})
}
}
然后,我们在同目录下的reducer.js 中case 为 “CHANGE_LIST” 做如下修改。
import { SEARCH_FOCUS, SEARCH_BLUR, CHANGE_LIST } from './actionTypes';
import { fromJS } from 'immutable';
const defaultState = fromJS({
focused: false,
list: [],
page: 1,
totalPage: 1
});
export default (state = defaultState, action) => {
switch (action.type) {
case SEARCH_FOCUS :
return state.set("focused", true);
case SEARCH_BLUR:
return state.set("focused", false);
case CHANGE_LIST:
return state.set("list", action.data).set("totalPage", action.totalPage);
default:
return state;
}
}
接下来,我们改一下Header 组件,使他每次最多显示十项热门搜索关键字。如下。由于 this.props.list 是immutable 对象,不能通过方括号的方式取值,因此,我们先使用 toJS() 方法将它转换为js对象。
getListArea = () => {
const {focused, list, page} = this.props;
const jsList = list.toJS();
const pageList = [];
for (let i = (page-1) * 10; i< page * 10; i++) {
pageList.push(<SearchInfoItem key={jsList[i]}>{jsList[i]}</SearchInfoItem>)
}
if (focused) {
return (
<SearchInfo>
<SearchInfoTitle>
热门搜索
<SearchInfoSwitch>换一批</SearchInfoSwitch>
</SearchInfoTitle>
<div>
{pageList}
</div>
</SearchInfo>
)
} else {
return null;
}
};
接着,我们需要修改一个点。热门搜索框框,并不仅在 搜索框获得焦点的时候出现。在鼠标在热门搜索框内时,也是出现的。鼠标移出这个框时会消失。
然后我们在 src/common/header/store 下的 reducer.js 中再定义一个变量 mouseIn,如下。
import { SEARCH_FOCUS, SEARCH_BLUR, CHANGE_LIST } from './actionTypes';
import { fromJS } from 'immutable';
const defaultState = fromJS({
focused: false,
list: [],
mouseIn: false,
page: 1,
totalPage: 1
});
export default (state = defaultState, action) => {
switch (action.type) {
case SEARCH_FOCUS :
return state.set("focused", true);
case SEARCH_BLUR:
return state.set("focused", false);
case CHANGE_LIST:
return state.set("list", action.data).set("totalPage", action.totalPage);
default:
return state;
}
}
然后,在src/comon/header 下的 index.js 中 SearchInfo 组件添加一个 onMouseEnter 方法,如下。
getListArea = () => {
const {focused, list, page, handleMouseEnter} = this.props;
const jsList = list.toJS();
const pageList = [];
for (let i = (page-1) * 10; i< page * 10; i++) {
pageList.push(<SearchInfoItem key={jsList[i]}>{jsList[i]}</SearchInfoItem>)
}
if (focused) {
return (
<SearchInfo onMouseEnter={handleMouseEnter}>
<SearchInfoTitle>
热门搜索
<SearchInfoSwitch>换一批</SearchInfoSwitch>
</SearchInfoTitle>
<div>
{pageList}
</div>
</SearchInfo>
)
} else {
return null;
}
};
再在 mapDispatchToProps 中添加上 handleMouseEnter 方法,如下。
const mapDispatchToProps = (dispatch) => {
return {
handleFocus () {
dispatch(actionCreators.getList());
dispatch(actionCreators.searchFocus());
},
handleBlur () {
dispatch(actionCreators.searchBlur());
},
handleMouseEnter () {
dispatch(actionCreators.mouseEnter());
}
}
}
再在 actionCreators.js 中创建这个action,如下。
export const mouseEnter = () => ({
type: actionTypes.MOUSE_ENTER
});
然后再去reducer.js 中设置一下,如下。
import { SEARCH_FOCUS, SEARCH_BLUR, CHANGE_LIST, MOUSE_ENTER } from './actionTypes';
import { fromJS } from 'immutable';
const defaultState = fromJS({
focused: false,
list: [],
mouseIn: false,
page: 1,
totalPage: 1
});
export default (state = defaultState, action) => {
switch (action.type) {
case SEARCH_FOCUS :
return state.set("focused", true);
case SEARCH_BLUR:
return state.set("focused", false);
case CHANGE_LIST:
return state.set("list", action.data).set("totalPage", action.totalPage);
case MOUSE_ENTER:
return state.set("mouseIn", true);
default:
return state;
}
}
然后,再更改一下显示热门搜索的条件,如下。
getListArea = () => {
const {focused, list, page, handleMouseEnter, mouseEnter} = this.props;
const jsList = list.toJS();
const pageList = [];
for (let i = (page-1) * 10; i< page * 10; i++) {
pageList.push(<SearchInfoItem key={jsList[i]}>{jsList[i]}</SearchInfoItem>)
}
if (focused || mouseEnter) {
return (
<SearchInfo onMouseEnter={handleMouseEnter}>
<SearchInfoTitle>
热门搜索
<SearchInfoSwitch>换一批</SearchInfoSwitch>
</SearchInfoTitle>
<div>
{pageList}
</div>
</SearchInfo>
)
} else {
return null;
}
};
同理,我们再设置一个moustOut 的属性放在redux 中,然后通过它控制热门搜索消失的条件之一。
如下是 src/common/header 下 的 index.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {
HeaderWrapper,
Logo,
Nav,
NavItem,
NavSearch,
Addition,
Button,
SearchWrapper,
SearchInfo,
SearchInfoTitle,
SearchInfoSwitch,
SearchInfoItem
} from './style';
import '../../statics/iconfont/iconfont.css';
import { CSSTransition } from 'react-transition-group';
import { actionCreators } from './store';
class Header extends Component {
render() {
let { focused, handleFocus, handleBlur} = this.props;
return (
<HeaderWrapper>
<Logo href='/'/>
<Nav>
<NavItem className='left active'>首页</NavItem>
<NavItem className='left'>下载</NavItem>
<NavItem className='right'>登录</NavItem>
<NavItem className='right'>
<span className="iconfont"></span>
</NavItem>
<SearchWrapper>
<CSSTransition
in={focused}
timeout={200}
classNames="slide"
>
<NavSearch
placeholder="搜索"
className={focused ? "focused" : ""}
onFocus={handleFocus}
onBlur={handleBlur}
></NavSearch>
</CSSTransition>
<span
className={focused ? "focused iconfont" : "iconfont"}
></span>
{this.getListArea()}
</SearchWrapper>
</Nav>
<Addition>
<Button className='writting'>
<span className="iconfont"></span>
写文章
</Button>
<Button className='reg'>注册</Button>
</Addition>
</HeaderWrapper>
)
}
getListArea = () => {
const {focused, list, page, handleMouseEnter, mouseEnter, handleMouseLeave} = this.props;
const jsList = list.toJS();
const pageList = [];
for (let i = (page-1) * 10; i< page * 10; i++) {
pageList.push(<SearchInfoItem key={jsList[i]}>{jsList[i]}</SearchInfoItem>)
}
if (focused || mouseEnter) {
return (
<SearchInfo
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<SearchInfoTitle>
热门搜索
<SearchInfoSwitch>换一批</SearchInfoSwitch>
</SearchInfoTitle>
<div>
{pageList}
</div>
</SearchInfo>
)
} else {
return null;
}
};
}
const mapStateToProps = (state) => {
return {
focused: state.get("header").get("focused"),
list: state.get("header").get("list"),
page: state.get("header").get("page"),
totalPage: state.get("header").get("totalPage"),
mouseEnter: state.get("header").get("mouseIn")
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleFocus () {
dispatch(actionCreators.getList());
dispatch(actionCreators.searchFocus());
},
handleBlur () {
dispatch(actionCreators.searchBlur());
},
handleMouseEnter () {
dispatch(actionCreators.mouseEnter());
},
handleMouseLeave () {
dispatch(actionCreators.mouseLeave());
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header);
下面是src/common/header/store 下的 actionCreators.js
import * as actionTypes from './actionTypes';
import axios from 'axios';
import { fromJS } from 'immutable';
import '../../../mockdata.js';
const changeList = (data) => ({
type: actionTypes.CHANGE_LIST,
data: fromJS(data),
totalPage: Math.ceil( data.length / 10 )
});
export const searchFocus = () => ({
type: actionTypes.SEARCH_FOCUS
});
export const searchBlur = () => ({
type: actionTypes.SEARCH_BLUR
});
export const getList = () => {
return (dispatch) => {
axios.get('/api/headerList.json').then( (res) => {
const data = res.data;
if( data.success ) {
dispatch( changeList( data.data ) )
}
}).catch( () => {
console.log("error");
})
}
}
export const mouseEnter = () => ({
type: actionTypes.MOUSE_ENTER
});
export const mouseLeave = () => ({
type: actionTypes.MOUSE_LEAVE
});
下面是src/common/header/store 下的reducer.js
import { SEARCH_FOCUS, SEARCH_BLUR,
CHANGE_LIST, MOUSE_ENTER,MOUSE_LEAVE
} from './actionTypes';
import { fromJS } from 'immutable';
const defaultState = fromJS({
focused: false,
list: [],
mouseIn: false,
page: 1,
totalPage: 1
});
export default (state = defaultState, action) => {
switch (action.type) {
case SEARCH_FOCUS :
return state.set("focused", true);
case SEARCH_BLUR:
return state.set("focused", false);
case CHANGE_LIST:
return state.set("list", action.data).set("totalPage", action.totalPage);
case MOUSE_ENTER:
return state.set("mouseIn", true);
case MOUSE_LEAVE:
return state.set("mouseIn", false);
default:
return state;
}
}
然后呢,我们可以去实现一下,点击“换一批”,热门搜索关键字就换成下十个。
先更改一下 Header 组件的代码,如下
getListArea = () => {
const {focused, list, page, handleMouseEnter,
mouseEnter, handleMouseLeave, handleChangeKeyWord, totalPage
} = this.props;
const jsList = list.toJS();
const pageList = [];
if (jsList.length) {
for (let i = (page-1) * 10; i< page * 10; i++) {
if (i >= jsList.length) {
break;
}
pageList.push(<SearchInfoItem key={jsList[i]}>{jsList[i]}</SearchInfoItem>)
}
}
if (focused || mouseEnter) {
return (
<SearchInfo
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<SearchInfoTitle>
热门搜索
<SearchInfoSwitch onClick={() => handleChangeKeyWord(page, totalPage)}>
换一批
</SearchInfoSwitch>
</SearchInfoTitle>
<div>
{pageList}
</div>
</SearchInfo>
)
} else {
return null;
}
};
const mapDispatchToProps = (dispatch) => {
return {
handleFocus () {
dispatch(actionCreators.getList());
dispatch(actionCreators.searchFocus());
},
handleBlur () {
dispatch(actionCreators.searchBlur());
},
handleMouseEnter () {
dispatch(actionCreators.mouseEnter());
},
handleMouseLeave () {
dispatch(actionCreators.mouseLeave());
},
handleChangeKeyWord (page, totalPage) {
let newPage = (page) % totalPage + 1;
dispatch(actionCreators.changekeyword(newPage));
}
}
}
然后改一下 actionCreators.js
export const changekeyword = (page) => ({
type: actionTypes.CHANGE_KEY_WORD,
data: page
});
再改改reducer.js
import { SEARCH_FOCUS, SEARCH_BLUR,
CHANGE_LIST, MOUSE_ENTER,
MOUSE_LEAVE, CHANGE_KEY_WORD
} from './actionTypes';
import { fromJS } from 'immutable';
const defaultState = fromJS({
focused: false,
list: [],
mouseIn: false,
page: 1,
totalPage: 1
});
export default (state = defaultState, action) => {
switch (action.type) {
case SEARCH_FOCUS :
return state.set("focused", true);
case SEARCH_BLUR:
return state.set("focused", false);
case CHANGE_LIST:
return state.set("list", action.data).set("totalPage", action.totalPage);
case MOUSE_ENTER:
return state.set("mouseIn", true);
case MOUSE_LEAVE:
return state.set("mouseIn", false);
case CHANGE_KEY_WORD:
return state.set("page", action.data);
default:
return state;
}
}
最后,我们把src/common/header/store 下的reducer 中 set 很多值时,用merge 方法代码,如下。
export default (state = defaultState, action) => {
switch (action.type) {
case SEARCH_FOCUS :
return state.set("focused", true);
case SEARCH_BLUR:
return state.set("focused", false);
case CHANGE_LIST:
return state.merge({
"list": action.data,
"totalPage": action.totalPage
});
case MOUSE_ENTER:
return state.set("mouseIn", true);
case MOUSE_LEAVE:
return state.set("mouseIn", false);
case CHANGE_KEY_WORD:
return state.set("page", action.data);
default:
return state;
}
}
Done.