关于immutable.js:immutable.js是一个第三方模块,可以帮助我们生成一个immutable对象,这个对象不可被变更。若state是一个immutable对象,state就不可以被改变,这样reducer就不会出问题。因此,我们就需要把state变成immutable对象。
immutable安装指令:npm i immutable --save ·
关于combineReducers:reducer如果存放过多的数据可能会造成代码的不可维护。那么,combineReducers帮我们把一个reducer拆成很多个子reducer,最终再做一个整合。
关于redux-thunk:Redux-thunk是redux的一个中间件,它使得我们可以在action里面写函数。中间件是指action和store之间,是对dispatch的升级。本案例使用redux-thunk发送ajax数据请求,获取数据后存到store中,在页面进行显示。
redux-thunk安装指令:npm i redux-thunk --save
1.1 在以下案例中,在header > store > actionCreators中,因为用了redux-thunk,函数返回函数,这也是thunk的意义。
//header > store > actionCreators
import { search_focus, search_blur, change_list } from "./actionTypes";
import { fromJS } from 'immutable';
import axios from "axios"
export const getFocusAction = () => ({
type: search_focus
})
export const getBlurAction = () => ({
type: search_blur
})
//因为reducer中的list在最外层包裹了fromJS,最初list数组就已经被变成了immutable类型的数组
//因此要在这里做统一
const changeListAction = (data) => ({
type: change_list,
data: fromJS(data)
})
//因为用了redux-thunk,函数返回函数,这也是thunk的意义
export const getList = () => {
return (dispatch) => {
//请求假数据 来自public > api > headerList.json
axios.get('/api/headerList.json').then((res) => {
const data = res.data;
const action = changeListAction(data.data)//将list中的内容传给此函数
dispatch(action)
}).catch(() => {
console.log("error");
})
}
}
1.2 把跟header相关的数据和操作放到了header > store > reducer.js中,存放的是header组件相关的默认数据和对数据的操作。
import { search_focus, search_blur, change_list } from "./actionTypes";
import { fromJS } from 'immutable'
const defaultState = fromJS({
focused: false,
list: []
});
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
}
}
2. 然后,在src > store > reducer.js中拿到header > store > reducer.js中的headerReducer,整合出一个大的reducer。这样,我们就把reducer里的内容拆成一小个一小个管理了。因为在src > store > index.js中创建了store,也应该在这里使用thunk。
// src > store > index.js
import { 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;
// src > store > reducer.js
import { combineReducers } from 'redux-immutable'
import headerReducer from '../common/header/store/reducer'
const reducer = combineReducers({
header: headerReducer
})
export default reducer;
3. header组件中因为combineReducers, “state.方法”要改为“state.header.方法”: src > common > header > index.js
import React, { Component } from "react";
import { connect } from 'react-redux';
import { CSSTransition } from "react-transition-group";
import { HeaderWrapper, Logo, Nav, NavItem, NavSearch, Addition, Button, SearchWrapper, SearchInfo, SearchInfoTitle, SearchInfoSwitch, SearchInfoItem, SearchInfoList } from "./style";
import { getFocusAction, getBlurAction, getList } from './store/actionCreators'
class Header extends Component {
getListArea(show) {
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;//什么都不返回
}
}
render() {
const { focused, handleInputFocus, handleInputBlur, list } = this.props
return (
<HeaderWrapper>
<Logo />
<Nav>
<NavItem className="left active">首页</NavItem>
<NavItem className="left">下载App</NavItem>
<NavItem className="right">登录</NavItem>
<NavItem className="right">
<i className="iconfont"></i>
</NavItem>
<SearchWrapper>
<CSSTransition
in={focused}
timeout={200}
classNames="slide">
<NavSearch
className={focused && 'focused'}
onFocus={() => handleInputFocus(list)}
onBlur={handleInputBlur}></NavSearch>
</CSSTransition>
<i className={focused ? 'i_focused iconfont' : 'iconfont'}></i>
{this.getListArea()}
</SearchWrapper>
</Nav>
<Addition>
<Button className="writing">
<i className="iconfont"></i>
写文章</Button>
<Button className="reg">注册</Button>
</Addition>
</HeaderWrapper>
)
}
}
const mapStateToProps = (state) => {
return {
//focused: state.focused 没用immutable时
focused: state.getIn(['header', 'focused']),//也可写为 state.get('header').get('focused')
list: state.getIn(['header', 'list'])
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleInputFocus(list) {
//改变focused数据的值变为true 作用是控制search框的显示和input框的长短
dispatch(getFocusAction());
//redux-thunk使得我们可以去action里做异步操作
//注意,当list.size等于0,也就是没数据时,才请求search的数据
(list.size === 0) && dispatch(getList());
},
handleInputBlur() {
dispatch(getBlurAction());
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header);