1. 程式人生 > >Redux在React中的使用

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

      合併成一個大的Reducer即可。

      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物件。

      mapDispatchToPropsconnect函式的第二個引數,用來建立 UI 元件的引數到store.dispatch方法的對映。也就是說,它定義了哪些使用者的操作應該當作 Action,傳給 Store。它可以是一個函式,也可以是一個物件。

      如果mapDispatchToProps是一個函式,會得到dispatchownProps(容器元件的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')
    );