Redux在React中的使用
redux
Redux是一個
資料狀態管理
外掛,當使用React或是vue開發元件化的SPA程式時,元件之間共享資訊是一個非常大的問題。例如,使用者登陸之後客戶端會儲存使用者資訊(ID,頭像等),而系統的很多元件都會用到這些資訊,當使用這些資訊的時候,每次都重新獲取一遍,這樣會非常的麻煩,因此每個系統都需要一個管理多元件使用的公共資訊的功能,這就是redux的作用。
簡單使用
import { createStore } from 'redux'
- 第一步:定義計算規則,即reducer
function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } }
- 第二步:根據計算規則生成store
let store = creatStore(counter);
- 第三步:定義資料(state)變化之後的派發規則
store.subscribe(() => {
console.log('fn1 -> current state', store.getState())
})
store.subscribe(() => {
console.log('fn2 -> current state', store.getState())
})
- 第四步:觸發資料變化
store.dispatch({
type: 'INCREMENT',
})
在React中使用
redux經常和react搭配使用,使用React開發系統,絕大部分都需要結合Redux來使用,下面介紹一下如何在React中使用redux.
前四步和之前的差不多,但是要注意結構的拆分。
匯入依賴
npm install redux --save
npm install react-redux --save
檔案目錄結構
reducers
index.js
Reducer 函式負責生成 State。由於整個應用只有一個 State 物件,包含所有資料,對於大型應用來說,這個 State 必然十分龐大,導致 Reducer 函式也十分龐大。若把傳入Reducer的Action改變為State的屬性,屬性間沒有關聯,我們就可以把Reducer函式拆分,不同函式負責處理不同屬性,最終用
combineReducers
import { combineReducers } from 'redux'; import userInfo from './userInfo' const rootRuducer = combineReducers({ userInfo, //userInfo是State的一個屬性,也是一個action方法,同名。若不想同名,可寫成a: doSomethingWithA的形式(a是State的屬性)。 }) export default rootRuducer;
userInfo.js
import * as actionTypes from '../constants/userInfo'; const initState = {} export default function userInfo (state = initState, action) { switch(action.type) { case actionTypes.UPDATE_CITYNAME: return action.data default: return state } }
actions
userInfo.js
import * as actionTypes from '../constants/userInfo'; export function updateCityName(data) { return { type: actionTypes.UPDATE_CITYNAME, data, } } export function XXX(){ return {} }
constants
userInfo.js
被多個地方引用,這樣可以方便修改
export const UPDATE_CITYNAME = 'UPDATE_CITYNAME';
store
configureStore.js
匯出建立store的函式
import { createStore } from 'redux'; import rootRuducer from '../reducers'; export default function configureStore(initState) { const store = createStore(rootRuducer, initState, window.devToolsExtension ? window.devToolsExtension() : undefined ); return store; }
containers
存放頁面的資料夾
index.js
mapStateToProps
會訂閱 Store,每當state
更新的時候,就會自動執行,重新計算 UI 元件的引數,從而觸發 UI 元件的重新渲染。mapStateToProps
的第一個引數總是state
物件,還可以使用第二個引數,代表容器元件的props
物件。mapDispatchToProps
是connect
函式的第二個引數,用來建立 UI 元件的引數到store.dispatch
方法的對映。也就是說,它定義了哪些使用者的操作應該當作 Action,傳給 Store。它可以是一個函式,也可以是一個物件。如果
mapDispatchToProps
是一個函式,會得到dispatch
和ownProps
(容器元件的props
物件)兩個引數,返回一個物件,該物件的每個鍵值對都是一個對映,定義了 UI 元件的引數怎樣發出 Action。bindActionCreators(actionCreators, dispatch)
把一個 value 為不同 action creator 的物件,轉成擁有同名 key 的物件。同時使用dispatch
對每個 action creator 進行包裝,以便可以直接呼叫它們。一般情況下你可以直接在
Store
例項上呼叫dispatch
。如果你在 React 中使用 Redux,react-redux 會提供dispatch
函式讓你直接呼叫它 。惟一會使用到
bindActionCreators
的場景是當你需要把 action creator 往下傳到一個元件上,卻不想讓這個元件覺察到 Redux 的存在,而且不希望把dispatch
或 Redux store 傳給它。import React from 'react'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import * as userInfoActionsFromOtherFile from '../actions/userInfo.js'; class App extends React.Component { constructor(props) { super(props); this.state = { initDone: false } } componentDidMount() { this.setState({ initDone: true, }) } render() { return ( <div> {this.state.initDone ? this.props.children : 'loadding...'} </div> ) } } function mapStateToProps(state) { return { userInfo: state.userinfo, } } function mapDispatchToProps(dispatch) { return { userInfoAction: bindActionCreators(userInfoActionsFromOtherFile, dispatch), } } export default connect( mapStateToProps, mapDispatchToProps // 定義了哪些使用者的操作應該當作 Action,傳給 Store )(App);
index.js
React-Redux 提供
Provider
元件,可以讓容器元件拿到state
。所以在入口資料夾中,先建立store,然後再把包裹一層。 import React from 'react'; import ReactDOM from 'react-dom'; // import { render } from 'react-dom'; import { Provider } from 'react-redux'; import configureStore from './store/configureStore'; import App from './containers//index'; const store = configureStore(); ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );