从零开始项目03-redux

箭头函数返回对象字面量

我的小盲点,详细内容请参阅MDN箭头函数(对象字面量)

var func = () => ({foo: 1});

redux和react-redux依赖

App.js引入provider

import React, { Component } from "react";
import { Globalstyle } from "./style.js";
import { GlobalFont } from "./statics/iconfont/iconfont.js";
import { Provider } from "react-redux";
import Header from "./common/header/index.js";
import store from "./store";

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <Globalstyle></Globalstyle>
        <GlobalFont/>
         <Header/>
      </Provider>
    );
  }
}

export default App;

创建store文件夹,写index.js和reducer.js

  • header组件中的state删除,放到reducer的defaultState中
  • 删掉handleBlur()、 handleFocus() 函数,因为不需要借助setState改变state了
  • 把所有this.state改成this.props,将两个函数,放在mapDispatchToProps(){}中
  • 写reducer函数,最后,header就变成了一个无状态组件,改用函数来写,效率更高
store的index.js文件夹内容最终如下:
import { legacy_createStore as createStore, compose } from "redux";
import reducer from "./reducer"

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer,composeEnhancers());
export default store;

一开始redux-devtools没生效,我也没注意到,其实redux-devtools生效的话,浏览器右上角的图标会点亮

生效的话,图标会亮

//Header组件的最终内容
import React from "react";
import { connect } from "react-redux";
import { HeaderWrapper, Logo, Nav, Navitem, NavSearch, Addition, Button, SearchWrapper } from "./style";
import { CSSTransition } from "react-transition-group";
const Header = (props) => {
    return (
        <HeaderWrapper>
            <Logo href="/" />
            <Nav>
                <Navitem className="left active">
                    <span className="iconfont">&#xe67d;</span>&nbsp;
                    首页
                </Navitem>
                <Navitem className="left">下载APP</Navitem>
                <SearchWrapper>
                    <CSSTransition
                        in={props.focused}
                        timeout={200}
                        classNames="slide"
                    >
                        <NavSearch
                            className={props.focused ? "focused" : ""}
                            onFocus={props.handleFocus}
                            onBlur={props.handleBlur}
                        ></NavSearch>
                    </CSSTransition>
                    <span className={props.focused ? "focused iconfont" : "iconfont"}>&#xe60c;</span>
                </SearchWrapper>

                <Navitem className="right">登录</Navitem>
                <Navitem className="right">
                    <span className="iconfont">&#xe636;</span>
                </Navitem>
            </Nav>
            <Addition>
                <Button className="writting">
                    <span className="iconfont">&#xe600;</span>
                    写文章
                </Button>
                <Button className="reg">注册</Button>
            </Addition>
        </HeaderWrapper >
    )
}
const mapStateToProps = (state) => {
    return {
        focused: state.focused
    }
}
const mapDispatchToProps = (dispatch) => {
    return {
        handleFocus() {
            const action = {
                type: "search_focus",
            };
            dispatch(action);
        },
        handleBlur() {
            const action = {
                type: "search_blur",
            };
            dispatch(action);
        }
    }
}
export default connect(mapStateToProps,mapDispatchToProps)(Header);
//reducer.js最终内容
const defaultState = {
    focused: false
}
export default (state = defaultState, action) => {
    let newState = { ...state };
    if (action.type == "search_focus") {
        newState.focused = true;
        return newState;
    }
    if (action.type == "search_blur") {
        newState.focused = false;
        return newState;
    }
    return state;
}
;

combineReducer的使用

使用原因:当数据量比较大的时候,reducer中的数据会非常庞杂
改进:各个组件专用的数据,就存放在该组件所在文件夹下的store中,最后再在总store中集中引用

  1. 在header文件夹下新建一个store文件,其中包含index.js和reducer.js
//index.js
import reducer from "./reducer"
export { reducer };//注意这里不能用default,否则会出错,暂时不清楚原因
//reducer.js文件就是原来的总的reducer,剪切过来的
const defaultState = {
    focused: false
}
export default (state = defaultState, action) => {
    let newState = { ...state };
    if (action.type == "search_focus") {
        newState.focused = true;
        return newState;
    }
    if (action.type == "search_blur") {
        newState.focused = false;
        return newState;
    }
    return state;
}
;
  1. 原来的总store的reducer.js应用combineReducers
import { combineReducers } from "redux"
import { reducer as headReducer } from "../common/header/store"

const reducer= combineReducers({
    header: headReducer
})
export default reducer;

这样原来的一层state结构变成了两层,多了一个header,表明这是专属于header的state
新的state结构
3. 修改header中index.js文件的mapStateToProps函数

const mapStateToProps = (state) => {
    return {
        focused: state.header.focused//原来是state.focused
    }
}

actionCreator和actionType(constants)文件的创建

  • 在header/store下创建actionCreator
export const searchFocus = () => ({
    type:"search_focus"
})
export const searchBlur = () => ({
    type: "search_blur"
})
  • 在header的index.js文件中引入,并修改
import * as actionCreator from "./store/actionCreator";
。。。
const mapDispatchToProps = (dispatch) => {
    return {
        handleFocus() {
            dispatch(actionCreator.searchFocus());
        },
        handleBlur() {
            dispatch(actionCreator.searchBlur());
        }
    }
}
  • 在header/store下创建actionType(constants)文件
export const SEARCH_FOCUS = "header/search_focus";
export const SEARCH_BLUR = "header/search_blur";
  • 修改header下的actionCreator、reducer文件
//actionCreator.js
import * as actionName from "./actionType";
export const searchFocus = () => ({
    type: actionName.SEARCH_FOCUS
})
export const searchBlur = () => ({
    type: actionName.SEARCH_BLUR
})
//reducer
import * as actionName from "./actionType";
const defaultState = {
    focused: false
}
export default (state = defaultState, action) => {
    let newState = { ...state };
    if (action.type == actionName.SEARCH_FOCUS) {
        newState.focused = true;
        return newState;
    }
    if (action.type == actionName.SEARCH_BLUR) {
        newState.focused = false;
        return newState;
    }
    return state;
};
  • 修改header/store/index.js文件,一般情况下,不希望在header/index中直接引用header/store/actionCreator.js,希望统一由header/store/index.js暴露出去
//header/store/index.js
import reducer from "./reducer";
import * as actionCreator from "./actionCreator";
import * as actionName from "./actionType";

export { reducer, actionCreator, actionName};

header/index更改引入方式:
原来:
import * as actionCreator from "./store/actionCreator";
现在:
import { actionCreator }from "./store";

使用immutable.js插件

原因

在reducer中,我们不能对state中的代码进行修改,通常是通过拷贝一份数据然后在拷贝数据中进行修改返回,但是有的时候不小心就会修改到state中的值,又很难发现其中的问题。
因此就需要Immutable.js来进行数据的管理。这是一个第三方的库,需要进行安装
immutable的意思是:不可改变的

修改代码

npm install immutable
修改header/store/reducer.js
import * as actionName from "./actionType";
import { fromJS } from "immutable";// 导入immutable模块中fromJS方法
//使用fromJS将其变为immutable对象数据(只能使用set、get)
const defaultState = fromJS({
    focused: false
})
  • 在header/index.js中需要使用immutable中的get方法才能来获取immutable对象的值。
    src/common/header/index.js
const mapStateToProps = (state) => {
    return {
        focused: state.header.get("focused")
    }
}

第三步:在header/store/reducer.js中使用immutable中的set方法修改immutable对象的值。

这个方法并不会直接修改我们state中的数据,这个方法的实质是:我们使用这个方法,会结合之前的immutable对象,和我们设置的值,来返回一个全新的值

src/common/header/store/reducer.js

import * as actionName from "./actionType";
import { fromJS } from "immutable";// 导入immutable模块中fromJS方法
//使用fromJS将其变为immutable对象数据(只能使用set、get)
const defaultState = fromJS({
    focused: false
})
export default (state = defaultState, action) => {
    //immutable对象的set方法,会结合之前immutable对象
    //和设置的值,会返回一个全新的对象
    if (action.type == actionName.SEARCH_FOCUS) {
        return state.set("focused",true);
    }
    if (action.type == actionName.SEARCH_BLUR) {
        return state.set("focused", false);
    }
    return state;
};

使用完毕!

使用redux-immutable库

为什么使用redux-immutable

在如下代码中src/common/header/index.js中mapStateToProps里面的focused: state.header.get("focused")这行代码,

const mapStateToProps = (state) => {
    return {
        focused: state.header.get("focused")
    }
}

其中states 是一个js对象,而state.header是一个immutable对象,所以调用focused属性的时候,得先调用state的‘.’,再去调用header的‘.get’方法。所以说数据获取不统一,前面是按照js对象来获取,后面又是按照immutable对象来获取的,这样不太靠谱。我们希望统一一下,因此我们希望state也是一个immutable对象。
state是在最外层的总的store的reducer中生成的,引入redux-immutable,使全局state变成immutable对象

引入npm install redux-immutable

全局store的reducer
//import { combineReducers } from "redux"修改成下面这句
import { combineReducers } from "redux-immutable"
import { reducer as headReducer } from "../common/header/store"
//引入redux-immutable,使全局state变成immutable对象
const reducer= combineReducers({
    header: headReducer
})
export default reducer;

因为全局state已经变为immutable对象,因此也要用get方法调用,修改header的index.js中的mapStateToProps函数

const mapStateToProps = (state) => {
    return {
    	//focused: state.header.focused原本语句
        //可以改为这样的语句:focused: state.get("header").get("focused")
        focused: state.getIn(["header","focused"])//最终版本
    }
}

关于immutable的使用,可以参阅其网站immutable官网文档
完毕!

增加热门搜索部分

静态

src/common/header/index.js中增加一个区域

import { HeaderWrapper, Logo, Nav, Navitem, NavSearch, Addition, Button, SearchWrapper, SearchInfo, SearchInfoTitle, SearchInfoSwitch, SearchInfoItem, SearchInfoList} from "./style";
。。。
<SearchInfo>
    <SearchInfoTitle>
        热门搜索
        <SearchInfoSwitch>换一批</SearchInfoSwitch>
    </SearchInfoTitle>
    <SearchInfoList>
        <SearchInfoItem>教育</SearchInfoItem>
        <SearchInfoItem>教育</SearchInfoItem>
        <SearchInfoItem>教育</SearchInfoItem>
        <SearchInfoItem>教育</SearchInfoItem>
    </SearchInfoList>
</SearchInfo>

src/common/header/style.js中增加对应的样式

export const SearchInfo = styled.div`
    position: absolute;
    left: 0;
    top: 56px;
    width: 240px;
    padding: 0 20px;
    box-shadow: 0 0 8px rgba(0, 0, 0, .2);
    background: #fff;
`
export const SearchInfoTitle = styled.div`
    margin-top: 20px;
    margin-bottom: 15px;
    line-height: 20px;
    font-size: 14px;
    color: #969696;
`;
export const SearchInfoSwitch = styled.span`
    float:right;
    font-size:13px;
`
export const SearchInfoList = styled.div`
    overflow: hidden;
`;
export const SearchInfoItem = styled.a`
    display: block;
    float: left;
    line-height: 20px;
    padding: 0 5px;
    margin-right: 10px;
    margin-bottom: 15px;
    font-size: 12px;
    border: 1px solid #ddd;
    color: #787878;
    border-radius: 3px;
`

结果如下图
在这里插入图片描述

动态

接下来添加一个函数,并加以改造。使之做到,鼠标focus on这个搜索框的时候,才显示”热门搜索“

const getListArea = (show) => {
    if (show) {
        return (
            <SearchInfo>
                <SearchInfoTitle>
                    热门搜索
                    <SearchInfoSwitch>换一批</SearchInfoSwitch>
                </SearchInfoTitle>
                <SearchInfoList>
                    <SearchInfoItem>教育</SearchInfoItem>
                    <SearchInfoItem>教育</SearchInfoItem>
                    <SearchInfoItem>教育</SearchInfoItem>
                    <SearchInfoItem>教育</SearchInfoItem>
                </SearchInfoList>
            </SearchInfo>
        )
    } else {
        return null;
    }
}
。。。
//原来的位置调用该函数即可
 {getListArea(props.focused)}

效果达成,但现在,数据是写死的,全是“教育”,接下来用AJAX从其他地方动态获取

AJAX

也就是说,需要在鼠标onFocus的时候发送ajax请求

 <NavSearch
	className={props.focused ? "focused" : ""}
	 onFocus={props.handleFocus}//发送ajax请求
	 onBlur={props.handleBlur}
></NavSearch>
const mapDispatchToProps = (dispatch) => {
    return {
        handleFocus() {//在这里发送ajax请求
            dispatch(actionCreator.searchFocus());
        },
        handleBlur() {
            dispatch(actionCreator.searchBlur());
        }
    }
}

一般这种异步请求,不会放在组件里面写,而是用redux-thunk放在action里面

引入依赖

(1)安装中间件 npm install redux-thunk,并配置thunk使用
(2)安装ajax数据 请求插件npm install axios

设置API,放数据

React框架寻找的特性:

1.src目录下看对应的路由

2.找不到,会到public目录下的api/headerList.json寻找 然后就会发送出来,因此我们就可以使用假数据

因此,我们在public目录下创建对应的文件
在这里插入图片描述

{
    "success": true,
    "data": ["前端","区块链","react"]
}

使用数据

第一步:在src/common/header/store/reducer中

首先,我们新创建了一个变量list。要注意的是:list中数组的类型也是immutable;因此,我们在更新数据的时候也要使得获取的数据是immutable类型的,因此我们最好在获取数据的时候将要传过去的data变为immutable对象(在actionCreators.js中)

import { constants } from './';
import { fromJS } from 'immutable';

//将其变为immutable
const defaultState = fromJS({
    focused: false,
    list: []
});
export default (state = defaultState, action) => {
    if(action.type === constants.SEARCH_FOCUS){
        return state.set('focused', true);
    }
   if(action.type === constants.SEARCH_BLUR){
        return state.set('focused', false);
    } 
    if(action.type === constants.CHANGE_LIST){
        return state.set('list', action.data);   
    }
    return state;
}

第二步:修改全局store,添加redux-thunk。
src/store/index.js

import { legacy_createStore as createStore, compose ,applyMiddleware} from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer"

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(
    applyMiddleware(thunk)
));
export default store;

第三步:我们要在搜索框聚焦的时候,获得Ajax数据。所以在src/common/header/index.js中派发action来获取Ajax数据(所以间接使用thunk中间件)

...
const mapDispatchToProps = (dispatch) => {
    return {
        handleFocus() {
            dispatch(actionCreator.getList());//新增语句,用来聚焦时发Ajax
            dispatch(actionCreator.searchFocus());
        },
        handleBlur() {
            dispatch(actionCreator.searchBlur());
        }
    }
}
...

第四步:src/common/header/store/actionCreator中定义getList方法

export const changeList = (data) => ({
    type: actionName.CHANGELIST,
    data: data.data
})
export const getList=()=>{
    return (dispatch) => {
        axios.get("/api/headerList.json").then((res) => {
            console.log(res.data);
            dispatch(changeList(res.data));
        }).catch(() => {
            console.log("error");
        })
    }
}

第五步:src/common/header/store/reducer中定义相应的action-state交互

import * as actionName from "./actionType";
import { fromJS } from "immutable";// 导入immutable模块中fromJS方法
//使用fromJS将其变为immutable对象数据(只能使用set、get)
const defaultState = fromJS({
    focused: false,
    list: []
})
export default (state = defaultState, action) => {
    //immutable对象的set方法,会结合之前immutable对象
    //和设置的值,会返回一个全新的对象
    if (action.type == actionName.SEARCH_FOCUS) {
        return state.set("focused",true);
    }
    if (action.type == actionName.SEARCH_BLUR) {
        return state.set("focused", false);
    }
    if (action.type == actionName.CHANGELIST) {//交互
        return state.set("list", action.data);
    }
    return state;
}
;

注意,到这里看上去似乎没问题了,但是,其实会有问题,immutable对象会把list:[]也变成immutable,因此也必须用immutable对象去替换,返回第四步,做如下修改:

src/common/header/store/actionCreator
import { fromJS } from "immutable";
export const changeList = (data) => ({
    type: actionName.CHANGELIST,
    data: fromJS(data.data)
})

第六步:修改src/common/header/index.js中的getListArea

   getListArea = () => {
        if (this.props.focused) {
            return (
                <SearchInfo>
                    <SearchInfoTitle>
                        热门搜索
                        <SearchInfoSwitch>换一批</SearchInfoSwitch>
                    </SearchInfoTitle>
                    <SearchInfoList>
                        {this.props.list.map((item) => {
                            return <SearchInfoItem key={ item }>{item}</SearchInfoItem>
                        })}
                        //注意,这里()=>{ return ...  },这个return作何解释?我还不懂,
                        //没有return会报错
                    </SearchInfoList>
                </SearchInfo>
            )
        } else {
            return null;
        }
    }

结果展示:
最终效果

代码优化微调

1.解构赋值

src/common/header/index.js

   getListArea = () => {
        const { focused, list } = this.props;
        if (focused) {
            return (
                <SearchInfo>
                    <SearchInfoTitle>
                        热门搜索
                        <SearchInfoSwitch>换一批</SearchInfoSwitch>
                    </SearchInfoTitle>
                    <SearchInfoList>
                        {list.map((item) => {
                            return <SearchInfoItem key={ item }>{item}</SearchInfoItem>
                        })}
                    </SearchInfoList>
                </SearchInfo>
            )
        } else {
            return null;
        }
    }
class Header extends Component{
    render() {
        const { focused, handleFocus, handleBlur } = this.props;
        return(
            <HeaderWrapper>
                <Logo href="/" />
                。。。
)}}

2 用switch代替if

export default (state = defaultState, action) => {
    //immutable对象的set方法,会结合之前immutable对象
    //和设置的值,会返回一个全新的对象
    switch (action.type) {
        case actionName.SEARCH_FOCUS:
            return state.set("focused", true);
            break;//这个函数内的所有break因为上面有return,所以都不会生效,但这个习惯最好还是保留
        case actionName.SEARCH_BLUR:
            return state.set("focused", false);
            break;
        case actionName.CHANGELIST:
            return state.set("list", action.data);
            break;
        default:
            return state;
    }
}
;

数据分页显示(实现换一批功能)

第一步:增加页码和总页数变量

import * as actionName from "./actionType";
import { fromJS } from "immutable";// 导入immutable模块中fromJS方法
//使用fromJS将其变为immutable对象数据(只能使用set、get)
const defaultState = fromJS({
    focused: false,
    list: [],
    page: 1,
    totalPage:1
})
export default (state = defaultState, action) => {
    //immutable对象的set方法,会结合之前immutable对象
    //和设置的值,会返回一个全新的对象
    switch (action.type) {
        case actionName.SEARCH_FOCUS:
            return state.set("focused", true);
            break;
        case actionName.SEARCH_BLUR:
            return state.set("focused", false);
            break;
        case actionName.CHANGELIST:
            return state.set("list", action.data).set("totalPage",action.totalPage);
            break;
        default:
            return state;
    }
};

修改changeList行为

export const changeList = (data) => ({
    type: actionName.CHANGELIST,
    data: fromJS(data),
    totalPage: Math.ceil(data.length / 10)//10组数据一页
})
export const getList=()=>{
    return (dispatch) => {
        axios.get("/api/headerList.json").then((res) => {
            dispatch(changeList(res.data.data));
        }).catch(() => {
            console.log("error");
        })
    }
}

第二步拿到当前页码

const mapStateToProps = (state) => {
    return {
        focused: state.getIn(["header", "focused"]),
        list: state.getIn(["header", "list"]),
        page:state.getIn(["header","page"])
    }
}

修改getListArea的内容,获取到当前page并且将显示范围限制在第一页
由于list为immutable对象,可以将它转换成JS语句,以便修改

获取到当前page
   getListArea = () => {
        const { focused, list, page } = this.props;
        const newList = list.toJS();//list是个immutable对象,这一语句可以将它转换成JS语句
        const pageList = [];//每次一调用,就会重置该数组
        for (let i = (page - 1) * 10; i < page * 10; i++){
            pageList.push(
                <SearchInfoItem key={newList[i]}>{newList[i]}</SearchInfoItem>
            )
        }
        if (focused) {
            return (
                <SearchInfo>
                    <SearchInfoTitle>
                        热门搜索
                        <SearchInfoSwitch>换一批</SearchInfoSwitch>
                    </SearchInfoTitle>
                    <SearchInfoList>
                        {pageList}
                    </SearchInfoList>
                </SearchInfo>
            )
        } else {
            return null;
        }
    }

第三步:热门搜索块实现失焦后仍不消失,但在鼠标移出后必定消失的功能

新增变量 mouseIn,热门搜索块的出现与否,应由 mouseIn和focused共同决定

const defaultState = fromJS({
    focused: false,
    mouseIn:false,
    list: [],
    page: 1,
    totalPage:1
})
const mapStateToProps = (state) => {
    return {
        focused: state.getIn(["header", "focused"]),
        list: state.getIn(["header", "list"]),
        page:state.getIn(["header","page"]),
        mouseIn: state.getIn(["header","mouseIn"])
    }
}

定义两个函数,负责改变mouseIn的状态

   getListArea = () => {
        const { focused, list, page, mouseIn, handleMouseEnter, handleMouseLeave } = this.props;
        const newList = list.toJS();
        const pageList = [];
        for (let i = (page - 1) * 10; i < page * 10; i++){
            pageList.push(
                <SearchInfoItem key={newList[i]}>{newList[i]}</SearchInfoItem>
            )
        }
        if (focused || mouseIn) {//热门搜索块的出现与否,由mouseIn和focused共同决定
            return (
                <SearchInfo
                    onMouseEnter={handleMouseEnter}
                    onMouseLeave={handleMouseLeave}
                >
                    <SearchInfoTitle>
                        热门搜索
                        <SearchInfoSwitch>换一批</SearchInfoSwitch>
                    </SearchInfoTitle>
                    <SearchInfoList>
                        {pageList}
                    </SearchInfoList>
                </SearchInfo>
            )
        } else {
            return null;
        }
    }
const mapDispatchToProps = (dispatch) => {
    return {
    	。。。
        handleMouseEnter() {
            dispatch(actionCreator.mouseEnter());
        },
        handleMouseLeave() {
            dispatch(actionCreator.mouseLeave());
        }
    }
}
export default (state = defaultState, action) => {
    switch (action.type) {
		。。。
        case actionName.MOUSE_ENTER:
            return state.set("mouseIn", true);
            break;
        case actionName.MOUSE_LEAVE:
            return state.set("mouseIn", false);
            break;
        default:
            return state;
    }
}

完成热门搜索块的出现与消失

第四步:定义换页函数

我的盲点: 函数如果想传参数,要写成箭头函数的形式
src/common/header/index

<SearchInfoSwitch onClick={() => { handleChangePage(page,totalPage) } }>换一批</SearchInfoSwitch>

src/common/header/index

  handleChangePage(page, totalPage) {
         if (page < totalPage) {
             dispatch(actionCreator.changePage(page+1));
         } else {
             dispatch(actionCreator.changePage(1));//回到第一页
         }   
     }

src/common/header/store/actionCreator

export const changePage = (newPage) => ({
    type: actionName.CHANGEPAGE,
    newPage
})

src/common/header/store/reducer

  case actionName.CHANGEPAGE:
         return state.set("page", action.newPage);
         break;

最后一点

我们实现代码之后,每次打开网页都会报一个警告
原因是什么?

const { focused, list, page, totalPage, mouseIn, handleMouseEnter, handleMouseLeave, handleChangePage } = this.props;
const newList = list.toJS();//一开始这是一个空数组
const pageList = [];
for (let i = (page - 1) * 10; i < page * 10; i++){
	 console.log(newList[i])//所以这里会发现,输出undefined
     pageList.push(
         <SearchInfoItem key={newList[i]}>{newList[i]}</SearchInfoItem>
     )
 }       

当我们加载网页,它就会渲染我们的网页,但是此时,我们没有获得AJAX数据,也就是说,我们的list中是没有值的,进而就导致key=undefined 就会报错。
解决的方法很简单,如下:当我们list有值的时候,我们的newList也有值,我们通过看newList.length它的长度来判断是否有值,只有存在值时,我们才会运行之后的for循环,这样就不会报错了

if (newList.length){
    for (let i = (page - 1) * 10; i < page * 10; i++){
        pageList.push(
            <SearchInfoItem key={newList[i]}>{newList[i]}</SearchInfoItem>
        )
    }
}

在这里插入图片描述

避免无意义的Ajax请求

当我们聚焦搜索框的时候,会发送AJAX的数据请求来获取数据,当我们关闭了,再打开,它会再次发送数据,实际上第一次聚焦的时候就已经把所有的数据都请求回来了,所以:
我们只需要获取一次数据就够了。

<NavSearch
    className={focused ? "focused" : ""}
    onFocus={() => { handleFocus(list) }}
    onBlur={handleBlur}
></NavSearch>
....
handleFocus(list) {
     if (list.size===0) {
         dispatch(actionCreator.getList());
     }
     dispatch(actionCreator.searchFocus());
 },

末尾修改一下换一批的cursor

export const SearchInfoSwitch = styled.span`
    cursor:pointer;//手形指针
    float:right;
    font-size:13px;
`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值