React 實踐項目 (二) redux + immutable + redux-saga

React在Github上已經有接近70000的 star 數了,是目前最熱門的前端框架。而我學習React也有一段時間了,如今就開始用 React+Redux 進行實戰!css

React 實踐項目 (一)html

本次實踐代碼前端

部署好的網址vue

上回說到用React寫了一個帶Header的首頁,咱們此次實踐就使用Redux進行狀態管理

index

Rudex

應用中全部的 state 都以一個對象樹的形式儲存在一個單一的 store 中。
唯一改變 state 的辦法是觸發 action,一個描述發生什麼的對象。
爲了描述 action 如何改變 state 樹,你須要編寫 reducers。node

咱們接下來開始開始進行登錄與註冊的狀態管理react

首先在 src 目錄下建立 redux 文件夾,目錄以下git

digag
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   └── favicon.ico
│   └── index.html
│   └── manifest.json
└── src
    └── components
        └── Index
            └── Header.js
            └── LoginDialog.js
            └── RegisterDialog.js
    └── containers
        └── App
            └── App.js
            └── App.css
    └── redux
        └── action
            └── users.js
        └── reducer
            └── auth.js
            └── users.js
        └── sagas
            └── api.js
            └── sagas.js
            └── selectors.js.js
            └── users.js
        └── store
            └── store.js
    └── App.test.js
    └── index.css
    └── index.js
    └── logo.svg
    └── registerServiceWorker.js

代碼可今後獲取github

記得在 package.json 中更新依賴json

接下來我會開始解釋關鍵代碼

  • action
    action/users.jsredux

/*
 * action 類型
 */
export const REGISTER_USER = 'REGISTER_USER';
// 省略其餘action 類型

/*
 * action 建立函數
 */
export const registerAction = (newUser) => {
  return{
    type:REGISTER_USER,
    data: newUser,
  }
};
// 省略其餘 action 建立函數
  • reducer
    reducer/users.js

//Immutable Data 就是一旦建立,就不能再被更改的數據。
//對 Immutable 對象的任何修改或添加刪除操做都會返回一個新的 Immutable 對象。
import Immutable from 'immutable';
//從 action 導入須要的 action 類型
import {REGISTER_USER, REGISTER_USER_SUCCESS, REGISTER_USER_FAILURE} from '../action/users';

// 初始化狀態
const initialState = Immutable.fromJS({
  newUser: null,
  error: null,
  saveSuccess: false,
});

//  reducer 就是一個純函數,接收舊的 state 和 action,返回新的 state。
export const users = (state = initialState, action = {}) => {
  switch (action.type) { // 判斷 action 類型
    case REGISTER_USER:  
      return state.merge({   // 更新狀態
        'newUser': action.data,
        'saveSuccess': false,
        'error': null,
      });
    case REGISTER_USER_SUCCESS:
      return state.set('saveSuccess', action.data);
    case REGISTER_USER_FAILURE:
      return state.set('error', action.data);
    default:
      return state
  }
};
  • store
    store/store.js

import {createStore, combineReducers, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga'
import * as reducer from '../reducer/users';

import rootSaga from '../sagas/sagas';

const sagaMiddleware = createSagaMiddleware();

const store = createStore(
  combineReducers(reducer),
  applyMiddleware(sagaMiddleware)
);

sagaMiddleware.run(rootSaga);

export default store;

而後在入口文件使用 store

src/index.js

import {Provider} from 'react-redux';
import store from './redux/store/store';
// 省略其餘

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>, document.getElementById('root')
);

在 App.js 中獲取 action 和 狀態

import {registerAction, loginAction} from '../../redux/action/users';
import {connect} from "react-redux";
import {bindActionCreators} from "redux";
 //省略其餘

class App extends Component {

  render(){
    return(
      <div className="App">
        //省略
      </div>
    )
  }

}

export default connect(
  (state) => {
// 獲取狀態   state.users  是指 reducer/users.js 文件中導出的 users
// 能夠 `console.log(state);` 查看狀態樹
  return { users: state.users }
},
  (dispatch) => {
  return {
// 建立action
    registerActions: bindActionCreators(registerAction, dispatch),
    loginActions: bindActionCreators(loginAction, dispatch),
  }
})(App);
// 在App 組件的props裏就有 this.props.users  this.props.registerActions this.props.loginActions 了
// 須要注意的是這裏this.props.users是Immutable 對象,取值須要用this.props.users.get('newUser') 
// 也可在 reducer 裏改用 js 普通對象

裝飾器版本:
須要在Babel中開啓裝飾器
裝飾器插件babel-plugin-transform-decorators-legacy

@connect(
  (state) => {
    console.log(state);
    return ({
      users: state.users,
    });
  },
  {registerActions: registerAction, loginActions: loginAction}
)

最後把 registerActions 傳給RegisterDialog子組件,

src/components/Index/RegisterDialog.js

// 省略其餘代碼
 handleSubmit = (e) => {
    e.preventDefault();
    // 驗證表單數據
    this.refs.user.validate((valid) => {
      if (valid) {
        // this.state.user 爲表單收集的 用戶註冊數據
        this.props.registerActions(this.state.user);
        this.setState({loading: true});
      }
    });
  };

流程是:

  • 調用 action

    `this.props.registerActions(this.state.user);`
     返回action 爲
{
    type:REGISTER_USER,
    data: this.state.user,
}
  • reducer 根據action類型更新狀態

switch (action.type) {
    case REGISTER_USER:
      return state.merge({
        'newUser': action.data,
        'saveSuccess': false,
        'error': null,
      });
//省略其餘代碼

這時咱們的store裏的狀態 newUser 就被更新爲 註冊彈窗裏收集的數據
到這裏都仍是同步的action,而註冊是一個異步的操做。
下篇文章會介紹如何使用 redux-saga 進行異步操做。
redux-saga 已經在使用了,有興趣的能夠自行查看代碼理解。

記得點star:)
項目代碼地址:https://github.com/DigAg/diga...
vue2版項目代碼地址:https://github.com/DigAg/diga...
相應後端項目代碼地址:https://github.com/DigAg/diga...

相關文章
相關標籤/搜索