項目中應用到了redux,根據前人的代碼和網上百度到的資料總結如下,以免遺忘。
簡介
redux是一個狀态管理工具,随着前端功能的增加,業務的複雜,将資料提取出元件是更好的方式,rudex是其中一種解決方案。
redux基本概念
redux由store、action、reducer 三部分組成。
store
store:倉庫,是一個包括項目所有的狀态資料的對象,redux提供了createStore函數來建立一個store
action
action:動作,是一種包括類型和資料的對象,是reducer函數的參數,為reducer提供參數來進行不同的處理,典型形式是:
{
type:"ADD_CART_NUM",
data: data
}
reducer
reducer:處理函數(我的翻譯),是一個具體的業務處理函數,位于action和disptch之間,被dispatch調用,根據傳入action對象資料進行處理并傳回一個新的state。典型形式是:
// 通過判斷Action的類型,傳回新的資料改變後的state對象,即使沒有任何狀态的改變,也要傳回一個對象
// 注意:傳回的state就是reducer提供的,也就是元件同步到的state
function counter(state = initState, action) { //state是reducer提供的預設值
const count = state.count
switch (action.type) {
case ADD_CART_NUM:
return { count: count + action.data.plusCount} //action.data是元件傳入的的值
default:
return state
}
}
dispatch
dispath: 分發函數(我的翻譯),是store内部的函數,傳入action并調用reducer,一般形式為:
<button onClick={()=>store.dispatch(myAction.increaseCartNum())}>Increase</button>
//其中increaseCartNum傳回一個action對象,在dispath内部進行對應reducer的調用
subscribe
subscribe: 注冊監聽函數(我的翻譯),也是store内部的函數,用于元件同步store中資料,一般形式為:
componentWillMount(){
// 訂閱狀态變化
store.subscribe((state)=>this.setState(state)) //注冊到監聽器函數到store中
}
問題 store中是如何區分不同元件的state呢?
redux流轉流程
假設store中資料位于上層,元件位于下層,那麼有資料上行和資料下行兩種流轉。
資料下行
store通過props傳遞到具體元件,元件通過this.props.xxx拿到資料。
資料上行
元件中點選按鈕等操作調用store的dispatch函數(對應的action作為函數參數),dispatch函數内部調用相應的recuder函數,改變并傳回store資料
react-reduce
在react應用中,利用react-redux來對兩者進行橋接使用。
react提供了一個函數和一個元件,簡化redux流程
connect函數:實作同步store資料到元件的props中、暴露action函數隐藏對dispatch調用reducer。一般形式為
// 綁定store中的狀态到元件的props
const mapStateToProps = state => {
return {
state
}
}
//暴露action到元件props
const mapDispatchToProps = dispath => {
return bindActionCreators({
changeCartNum
}, dispath)
}
export default connect(mapStateToProps, mapDispatchToProps)(MiniCart) //其中MiniCart是元件
Provider标簽用于将store傳遞到每個元件中,一般形式為:
ReactDOM.render(
<Provider store={store} >
<Router>
<LocaleProvider locale={zh_CN}><App /></LocaleProvider>
</Router>
</Provider >
, document.getElementById('root'));
整體代碼
//action.js aciton檔案
export function changeCartNum(data) {
return {
type: 'CART_NUM',
data
}
}
//frontReducer.js 子reducer檔案,不同子產品分為不同reducer檔案
const init = { //reducer的初始狀态
}
const Front = (state = init, action = {}) => {
switch (action.type) {
case 'CART_NUM':
//這裡可以添加一些處理函數,
return { ...state, ...action.data }
}
}
export default Front
//reducers.js 總reducers檔案
import { combineReducers } from 'redux'
import Front from './FrontReducer';
const reducer = combineReducers({ //合并reducer檔案
Front,
xxx,//不同的reducer檔案
xxx,//不同的reducer檔案
})
export default reducer
//index.js檔案
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store} > //傳遞store到内部元件中
<Router>
<LocaleProvider locale={zh_CN}><App /></LocaleProvider>
</Router>
</Provider >
, document.getElementById('root'));
//B元件擷取cartNumber
import { changeCartNum } from '@actions';
class PublicTop extends React.Component {
render(){
....
<span >
購物車數量
{
this.props.state.Front.cartNum
}
</span>
.....
}
}
const mapStateToProps = state => {
return {
state
}
}
const mapDispatchToProps = dispath => {
return bindActionCreators({
changeCartNum
}, dispath)
}
export default connect(mapStateToProps, mapDispatchToProps)(MiniCart)
//A元件設定cartNumber
import { changeCartNum } from '@actions'; // 購物車數量
class Cart extends Component {
render(){
.......
//增加購物車數量按鈕的點選處理函數
this.props.changeCartNum({ cartNum: totals })//totals是設定的值
.......
}
}
const mapStateToProps = state => {
return {
state
}
}
const mapDispatchToProps = dispath => {
return bindActionCreators({
changeCartNum
}, dispath)
}
export default connect(mapStateToProps, mapDispatchToProps)(Cart)
redux原生操作
以下程式是沒有使用react-redux的原生操作,包括store的簡易實作,用于了解redux的實作。
//reducer檔案 counterReducer.js
// 提供一個初始的狀态
initState={
count: 0
}
// 通過判斷Action的類型,傳回新的資料改變後的state對象,即使沒有任何狀态的改變,也要傳回一個對象
export default function counter(state = initState, action) {
const count = state.count
switch (action.type) {
case INCREMENT:
return { count: count + 1 }
default:
return state
}
}
//主檔案 index.js
// 建立一個store全局管理state和操作
const store = createStore(reducer);
// Provider在根元件<App>外面包了一層,App的所有子元件就預設都可以拿到store,通過元件的props傳遞
export default class Root extends Component {
render() {
return (
<Provider store={store}>
<App/>
</Provider>
)
}
}
// createStore的簡單實作
function createStore = ( reducer ) => {
let currentState; // 内部的狀态
let listeners = []; //所有的監聽者
const getState = () => currentState; // 擷取store中的state
// dispatch的操作就是内部執行reducer()函數,action和reducer在這兒産生交集,并且通知所有的監聽者
const dispatch = ( action ) => {
currentState = reducer(state, action); // 更新state
listeners.forEach(listener => listener());
}
// 訂閱事件
const subscribe = ( listener ) => {
listeners.push(listener);
return ()=>{
listeners = listeners.filter(l => l !== listener)
}
}
return {
getState,
dispatch,
subscribe
}
}
//元件檔案
class Counter extends Component{
componentWillMount(){
// 訂閱狀态變化
store.subscribe((state)=>this.setState(state)) //注冊到監聽器函數到store中
}
render() {
return (
<div>
<span>{value}</span>
//點選後dispatch事件類型
<button onClick={()=>store.dispatch(increaseAction.increase())}>Increase</button>
</div>
)
}
}