總結一下react專案中redux的基本使用方法,順便將其與vuex稍作對比學習,前端路漫漫
做一個麵包屑title跟隨選單欄名稱變換的功能
1.定義目錄
2.定義reducer
redux中的reducer類比vuex的mutations,都是更改state,不同點在於redux需要將新的state返回,vuex不需要
,多個reducer可以分多個js檔案,然後在index.js中通過combineReducers將reducer合併,類似vuex的Modules,例項程式碼:
// reducer/index.js
import { combineReducers } from 'redux'
import menu from './menu' // 引入單獨的reducer子模組
export default combineReducers({
menu,
// 可以繼續新增其他的reducer模組
})
注意,模組化後,元件中獲取state的值的時候,需要從對應的模組裡去取,例如:state.menu.menuName
// reducer/menu.js
import { type } from '../action';
const initialState = {
menuName: '首頁' // 初始化一個menuName的值
};
export default (state = initialState, action) => {
switch (action.type) {
case type.SWITCH_MENU:
return {
...state, // 需要將之前的state解構出來一起返回
menuName: action.menuName // 更新新的資料返回
};
break;
default:
return {...state};
}
}
3.定義action
redux的action,是定義需要呼叫那個reducer,主要是根據type欄位,對應reducer的action.type,同時也可以傳遞一些引數,跟vuex的action一樣,提交的是一個mutation(reducer),通過mutation(reducer)去改變state的值,例項程式碼:
// action/index.js
export const type = { // 統一管理所有的事件型別,方便專案整體的維護
SWITCH_MENU : 'SWITCH_MENU'
};
export function switchMenu(menuName) { //具體的某一個action方法,用於改變某一個state的值
return {
type: type.SWITCH_MENU,
menuName
}
}
4.定義store
import { createStore } from "redux";
import reducer from '../reducer';
import { composeWithDevTools } from 'redux-devtools-extension'; //chrome除錯工具
export default () => createStore(reducer,composeWithDevTools())
5.配置專案入口檔案
import { Provider } from "react-redux"; // redux提供的高階元件,高階元件類似vue中的mixin
import store from './redux/store';
ReactDOM.render(
// 將store傳入Provider,目的是使專案全域性都能拿到store,將其作用域提升到全域性
<Provider store={store()}>
<Router />
</Provider>,
document.getElementById('root')
);
6.元件中獲取state的值
每個用到redux的頁面或者元件都需要使用redux的Api匯出元件
import { connect } from 'react-redux'; // 可以理解其表面意思,將其元件和redux連線起來
// 中間省略一萬字...
export default connect(mapStateToProps,null)(Header)
connect
方法返回一個方法,返回的方法的引數裡傳入當前需要使用到redux的類元件或者說函式元件,connect
方法第一個引數是mapStateToProps
,這個回撥方法用於獲取state的值,connect回撥回來會給mapStateToProps
方法把state
傳過來;
第二個引數是mapDispatchToProps
,用於dispath一個action,跟vuex的套路差不多,同樣,connect
方法回撥的時候會把dispath方法回撥傳進來。
兩個引數非必傳,用到哪個傳哪個,沒用到可以傳null
;
還有一個重要的概念就是,這兩個回撥必須有返回,可想而知,接收state
的肯定要返回一個state
,提交action
方法的肯定要返回一個用於提交action
,改變state
的方法;返回值需要從this.props
中獲取
例項程式碼如下:
import React, {Component} from 'react';
import { Row, Col } from "antd";
import './index.less';
import { dates } from '../../utils';
import { connect } from 'react-redux';
class Header extends Component {
componentWillMount() {
this.setState({
userName: '小聰忙'
})
let timer = setInterval(() => {
let sysTime = dates.formatDate(Date.now());
this.setState({
sysTime
})
},1000)
this.setState({
timer
})
}
componentWillUnmount() {
this.setState({
timer: null
})
}
// 當前title的值從this.props.menuName中獲取
render() {
return (
<div className="header">
<Row className="header-top">
<Col span="24">
<span>歡迎,{this.state.userName}</span>
<a href="#">退出</a>
</Col>
</Row>
<Row className="breadcrumb">
<Col span="4" className="breadcrumb-title">
{this.props.menuName}
</Col>
<Col span="20" className="weather">
<span className="date">{this.state.sysTime}</span>
<span className="weather-detail">晴轉多雲</span>
</Col>
</Row>
</div>
);
}
}
//定義一個用於接收state裡某個值的回撥函式,必須有返回值,才能從props中獲取到這個值
const mapStateToProps = state => {
return {
// 特別注意,使用過vuex模組化開發的就應該知道,獲取state的值的時候也需要從對應的模組中去獲取
menuName: state.menu.menuName
}
};
export default connect(mapStateToProps,null)(Header)
7.改變state的值
import React, {Component} from 'react';
import './index.less';
import { Menu } from 'antd';
import menuConfig from '../../config/menuConfig';
import { NavLink } from "react-router-dom";
import { connect } from 'react-redux';
import { switchMenu } from '../../redux/action';
const { SubMenu } = Menu;
class NavLeft extends Component {
componentWillMount() {
const menuTreeNode = this.renderMenu(menuConfig);
this.setState({
menuTreeNode
})
}
renderMenu = (menuConfig) => {
return menuConfig.map((item,index) => {
if (item.children && item.children.length) {
return (
<SubMenu title={item.title} key={item.key} onClick={this.handleClick}>
{this.renderMenu(item.children)}
</SubMenu>
)
}
return (
<Menu.Item title={item.title} key={item.key}>
<NavLink to={item.key}>
{item.title}
</NavLink>
</Menu.Item>
)
})
}
// this.props中獲取mapDispatchToProps回撥返回的方法
render() {
const { handleClick } = this.props;
return (
<div>
<div className="logo">
<img src="/assets/logo-ant.svg" alt=""/>
<h1>Imooc ms</h1>
</div>
<Menu theme="dark" onClick={handleClick.bind(this)}>
{this.state.menuTreeNode}
</Menu>
</div>
);
}
}
// 回撥接收兩個引數,第一個是dispath方法,第二個是`ownProps`,它代表元件本身的props,如果寫了第二個引數`ownProps`,那麼當`prop`發生變化的時候,`mapStateToProps`也會被呼叫
const mapDispatchToProps = (dispatch, ownProps) => {
return {
handleClick: ({item}) => dispatch(switchMenu(item.props.title))
}
}
export default connect(null,mapDispatchToProps)(NavLeft);
最終效果圖: