メモ

22620 ワード

React状態管理ツール
Redux
1.Reduxコア
1.1 Redux紹介
JavaScript状態容器は、予測可能な状態管理を提供します.
1.2 Reduxコア概念とフロー
Store:格納状態のコンテナ、JavaScriptオブジェクトView:ビュー、HTMLページアクション:オブジェクト、状態に対してどのような操作が行われているかを記述し、Reducers:関数、操作状態を説明し、新しい状態に戻ります.
1.3 Redux使用:カウンタケース



  
  
  Redux


  
  0
  

  
  

    // 3.       
    const initialState = {
      count: 0
    }

    // 2.    reducer   
    function reducer(state = initialState, action) {
      switch (action.type) {
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        default:
          return state;
      }
    }

    // 1.    store   
    const store = Redux.createStore(reducer);

    // 4.    action 
    const increment = { type: 'increment' }
    const decrement = { type: 'decrement' }

    // 5.               
    document.getElementById('minus')
    .addEventListener('click', function () {
      // 6.   dispatch     action 
      store.dispatch(decrement)
    })
    document.getElementById('plus')
    .addEventListener('click', function () {
      // 6.   dispatch     action 
      store.dispatch(increment)
    })

    //   store {dispatch: ƒ, subscribe: ƒ, getState: ƒ, replaceReducer: ƒ, Symbol(observable): ƒ}
    console.log(store)
    //    store        state
    console.log(store.getState());

    //        
    store.subscribe(() => {
      console.log(store.getState())
      document.getElementById('count').innerHTML = store.getState().count
    });
  

1.4 ReduxコアAPI
//    store   
const store = Redux.createStore(reducer);
// 2.    reducer   
function reducer(state = initialState, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}
//    store        state
store.getState()
//        
store.subscribe(() => {
  console.log(store.getState())
});
//   dispatch     action 
store.dispatch(increment)
2.React+Redux
2.1 ReactでReduxを使わない時に発生した問題
Reactではコンポーネント通信のデータストリームは一方向であり、トップのコンポーネントはProps属性によって下部のコンポーネントにデータを転送することができ、下部のコンポーネントは上部のコンポーネントにデータを転送することができない.下のコンポーネントでデータを修正するには、上のコンポーネントから下のコンポーネントに修正データを転送する方法が必要です.プロジェクトがますます大きくなると、コンポーネント間のデータ転送が難しくなります.
2.2 ReactプロジェクトにReduxを加えるメリット
Reduxを使用してデータを管理し、Storeはコンポーネントと独立しているため、データ管理がコンポーネントと独立し、コンポーネントとコンポーネントの間でデータを転送するのが困難な問題を解決した.
2.3ダウンロードRedux
npm install redux react-redux
2.4 Reduxワークフロー
  • コンポーネントは、dispach方法によってアクションをトリガする
  • .
  • Storeはアクションを受け取り、アクションをReducer
  • に配信する.
  • Reducerは、アクションタイプに従って状態を変更し、変更された状態をStore
  • に戻す.
  • コンポーネントはStoreの状態を購読しています.Storeの状態更新はコンポーネント
  • に同期されます.
    2.5 Redux使用手順
    2.5.1 storeを作成する
    // src/store/index.js
    import { createStore } from 'redux'
    import reducer from './reducers/counter.reducer'
    export const store = createStore(reducer)
    ルートコンポーネントにstoreを使用する.
    import React from 'react';
    import ReactDOM from 'react-dom';
    import Counter from './components/Counter'
    import { Provider } from 'react-redux'
    import {store} from './store'
    /**
     * react-redux
     * Provider
     * connect
     */
    
    ReactDOM.render(
      //    provider   , store                 
      
        
      ,
      document.getElementById('root')
    );
    2.5.2 reducerを作成する
    // src/store/reducers/counter.reducer.js
    import { DECREMENT, INCREMENT } from "../count/counter.const";
    
    const initialState = {
      count: 0
    }
    
    export default function reducer (state = initialState, action) {
      switch (action.type) {
        case INCREMENT:
          return { count: state.count + 1 };
        case DECREMENT:
          return { count: state.count - 1 };
        default:
          return state;
      }
    }
    
    // src/store/count/counter.const.js
    export const INCREMENT = 'increment'
    export const DECREMENT = 'decrement'
    2.5.3コンポーネントの中でconnectを使ってstoreの中のstateとdispatchを受け入れます.connect方法は、2つのパラメータを受け取り、高次のコンポーネントを返す.connect方法の最初のパラメータはmapStateToProps方法であり、storeのstateをコンポーネントのpropsに伝達し、mapStateToProps方法のパラメータはstateであり、戻り値はオブジェクトであり、コンポーネントに伝達される.
    const mapStateToProps = (state) => ({
      count: state.count,
      a: 'a', //       ,             
    })
    connect方法の2番目のパラメータはmapDispatchToProps方法であり、storedispatchをコンポーネントのpropsに伝達し、mapDispatchToProps方法のパラメータはdispatchであり、戻り値はオブジェクトであり、オブジェクト内の方法はdispatchを使用してもよく、このオブジェクト内の方法はコンポーネントに伝達される.
    const mapDispatchToProps = (dispatch) => ({
      increment () {
        dispatch({ type: 'increment'})
      },
      decrement () {
        dispatch({ type: 'decrement' })
      }
    })
    また、reduxbindActionCreatorsを介して、action関数を作成しても良いです.
    import {bindActionCreators} from 'redux'
    
    // bindActionCreators        
    const mapDispatchToProps = dispatch => (
      //   
      ...bindActionCreators({
        increment () {
          return { type: 'increment'}
        },
        decrement () {
          return { type: 'decrement'}
        }
      }, dispatch)
    )
    または作成:
    const mapDispatchToProps = dispatch => bindActionCreators({
        increment () {
          return { type: 'increment'}
        },
        decrement () {
          return { type: 'decrement'}
        }
      }, dispatch)
    bindActionCreatorsの最初のパラメータをオフにしてもよい.
    import * as counterActions from '../store/actions/counter.actions'
    
    const mapDispatchToProps = dispatch => bindActionCreators(conterActions, dispatch)
    // src/store/actions/counter.actions.js
    import { DECREMENT, INCREMENT } from "../count/counter.const"
    
    export const increment = () => ({type: INCREMENT})
    export const decrement = () => ({type: DECREMENT})
    connect方法は、mapStateToPropsおよびmapDispatchToPropsを受け取り、高次のコンポーネントに戻り、Counterのコンポーネントに導かれる.
    export default connect(mapStateToProps, mapDispatchToProps)(Counter)
    
    最終コンポーネントコードは以下の通りです.
    // src/components/Counter.js
    import React from 'react'
    import {connect} from 'react-redux'
    import {bindActionCreators} from 'redux'
    import * as counterActions from '../store/actions/counter.actions'
    
    function Counter ({count, increment, decrement}) {
      return (
        
    {count}
    ) } // 1. connect store, store , // 2. connect store , props // 3. connect dispatch const mapStateToProps = (state) => ({ count: state.count, a: 'a', // , }) const mapDispatchToProps = dispatch => bindActionCreators(counterActions, dispatch) export default connect(mapStateToProps, mapDispatchToProps)(Counter)
    2.5.4 action伝達パラメータ
  • リレーパラメータ
  • は、パラメータを受け取り、reducer
    export const increment = payload => ({type: INCREMENT, payload})
    export const decrement = payload => ({type: DECREMENT, payload})
  • を伝える.
  • reducerは、受信したデータに基づいて
    export default function reducer (state = initialState, action) {
      switch (action.type) {
     case INCREMENT:
       return { count: state.count + action.payload };
     case DECREMENT:
       return { count: state.count - action.payload };
     default:
       return state;
      }
    }
  • を処理する.
    2.6 reduxポップアップブロックの判例を実現するstore中の状態が多いほど、reducer中のswitch分岐は多くなり、維持に不利で、reducerを分割する必要がある.
    src/index.js
    // src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import { Provider } from 'react-redux'
    import {store} from './store'
    
    ReactDOM.render(
      //    provider   , store                 
      
        
      ,
      document.getElementById('root')
    );
    src/store/index.js
    // src/store/index.js
    import { createStore } from 'redux'
    import reducer from './reducers/counter.reducer'
    
    export const store = createStore(reducer)
    src/store/reducers/counter.reducer.js
    // src/store/reducers/counter.reducer.js
    import { DECREMENT, INCREMENT } from "../const/counter.const";
    import { HIDEMODAL, SHOWMODAL } from "../const/modal.const";
    
    const initialState = {
      count: 0,
      show: false
    }
    
    export default function reducer (state = initialState, action) {
      switch (action.type) {
        case INCREMENT:
          return { ...state, count: state.count + action.payload };
        case DECREMENT:
          return { ...state, count: state.count - action.payload };
        case SHOWMODAL:
          return { ...state, show: true };
        case HIDEMODAL:
          return { ...state, show: false };
        default:
          return state;
      }
    }
    src/App.js
    // src/App.js
    import Modal from './components/Modal'
    import Counter from './components/Counter'
    
    function App() {
      return (
        
    ); } export default App;
    src/components/Modal.js
    // src/components/Modal.js
    import React from 'react'
    import { connect } from 'react-redux'
    import { bindActionCreators } from 'redux'
    import * as modalActions from '../store/actions/modal.actions'
    
    function Modal ({ showStatus, show, hide }) {
      const styles = {
        display: showStatus ? 'block': 'none',
        width: 200,
        height: 200,
        position: 'absolute',
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
        margin: 'auto',
        backgroundColor: 'skyblue'
      }
      return (
        
    ) } const mapStateToProps = state => ({ showStatus: state.show }) const mapDispatchToProps = dispatch => bindActionCreators(modalActions, dispatch) export default connect(mapStateToProps, mapDispatchToProps)(Modal)

    src/store/actions/modal.action.js

    // src/store/actions/modal.action.js
    import { HIDEMODAL, SHOWMODAL } from "../const/modal.const"
    
    export const show = () => ({ type: SHOWMODAL })
    export const hide = () => ({ type: HIDEMODAL })
    src/store/const/modal.co nst.js
    // src/store/const/modal.const.js
    export const SHOWMODAL = 'showModal'
    export const HIDEMODAL = 'hideModal'
    2.7分割reducerreducerによって提供されるツールcombineReducersを使用して、それぞれの小さなreducerをマージする.
    src/store/reducers/root.reducer.js
    // src/store/reducers/root.reducer.js
    import {combineReducers} from 'redux'
    import CounterReducer from './counter.reducer'
    import ModalReducer from './modal.reducer'
    
    // { counter: { count: 0 }, modal: { show: false } }
    export default combineReducers({
      counter: CounterReducer,
      modal: ModalReducer
    })
    src/store/reducers/counter.reducer.js
    // src/store/reducers/counter.reducer.js
    import { DECREMENT, INCREMENT } from "../const/counter.const";
    
    const initialState = {
      count: 0,
    }
    
    export default function counterReducer (state = initialState, action) {
      switch (action.type) {
        case INCREMENT:
          return { ...state, count: state.count + action.payload };
        case DECREMENT:
          return { ...state, count: state.count - action.payload };
        default:
          return state;
      }
    }
    src/store/reducers/modal.reducer.js
    // src/store/reducers/modal.reducer.js
    import { HIDEMODAL, SHOWMODAL } from "../const/modal.const";
    const initialState = {
      show: false
    }
    
    export default function modalReducer (state = initialState, action) {
      switch (action.type) {
        case SHOWMODAL:
          return { ...state, show: true };
        case HIDEMODAL:
          return { ...state, show: false };
        default:
          return state;
      }
    }
    storeを作成するときに入ってきたreducerは、私たちが先ほど定義したroot.reducer.jsから来ています.
    import { createStore } from 'redux'
    import RootReducer from './reducers/root.reducer'
    
    export const store = createStore(RootReducer)
    各コンポーネントのmapStateToPropsにおいても、対応する変更が発生する(state.counterおよびstate.modal).
    const mapStateToProps = (state) => ({
      count: state.counter.count,
    })
    const mapStateToProps = state => ({
      showStatus: state.modal.show
    })
    3.Redux中間部品
    3.1中間部品とは何ですか?
    中間価格は私達がreduxアプリケーションを拡張し、強化することができます.
    3.2 Redux中間部品の開発
    中間部品のテンプレートを開発する
    export default store => next => action => {  }
    3.3中間部品を登録する
    中間部品は開発が完了した後、登録されてこそ、Reduxのワークフローで有効になります.
    src/store/index.js
    // src/store/index.js
    import { createStore, applyMiddleware } from 'redux'
    import logger from './middlewares/logger'
    
    createStore(reducer, applyMiddleware(
      logger
    ))
    src/store/middleware/logger.js
    const logger = store => next => action => {
      console.log(store)
      console.log(action)
      next(action) //         next(action)
    }
    export default logger
    複数の中間部品を登録する場合、中間部品の実行順序は登録順序です.
    createStore(reducer, applyMiddleware(
      logger,
      test
    ))
    実行順序は、loggerミドルウェアを先に実行し、testミドルウェアを実行することである.中間部品の終わりにnext(action)を起動しないと、フロー全体はここでカードになります.これ以上実行しません.
    3.4 Redux中間部品開発例thunk(非同期中間部品)
    現在のこの中間関数は、どのような非同期操作を実行したいのかに関心がなく、ただ非同期操作を実行しているかに関心があります.もしあなたが実行しているのが非同期操作であれば、actionをトリガする時に、関数を伝えてください.もし実行しているのが同期操作なら、actionオブジェクトを伝達します.非同期の操作コードは、あなたが送ってきた関数の中に書いてください.この中間部品の関数は、あなたが送ってきた関数を呼び出した時に、dispach方法を過去のsrc/store/middleware/thunk.jsに伝えます.
    // src/store/middleware/thunk.js
    import { DECREMENT, INCREMENT } from "../const/counter.const";
    
    const thunk = ({dispatch}) => next => action => {
      if (typeof action === 'function') {
        return action(dispatch) // action           dispatch 
      }
      next(action)
    }
    export default thunk
    非同期関数actionをactionファイルに定義します.
    src/store/actions/modal.actions.js
    // src/store/actions/modal.actions.js
    import { HIDEMODAL, SHOWMODAL } from "../const/modal.const"
    
    export const show = () => ({ type: SHOWMODAL })
    export const hide = () => ({ type: HIDEMODAL })
    
    export const show_async = () => dispatch => {
      setTimeout(() => {
        dispatch(show())
      }, 2000);
    }
    元々はshowを使っていたところをshow_asyncに切り替え、非同期の機能を実現しました.
    4.Redux常用ミドルウェア
    4.1 redux-thunk
    4.1.1 redux-thunkダウンロード
    npm install redux-thunk
    4.1.2 redux-thunkの導入
    import thunk from 'redux-thunk';
    4.1.3 redux-thunkを登録する
    import { applyMiddleware } from 'redux'
    createStore(rootReducer, applyMiddleware(thunk));
    4.1.4 redux-thunk中間部品を使用する
    const loadPosts = () => async dispatch => {
      const posts = await axios.get('/api/posts').then(response => response.data);
      dispatch({type: LOADPOSTSSUCCE, payload: posts});
    }
    4.2 redux-saga
    4.2.1 redux-saga解決の問題redux-sagaは、非同期動作をAction Creatorファイルから引き出して、別個のファイルに置くことができる.
    4.2.2ダウンロードredux-saga
    npm install redux-saga
    4.2.3レdux-sagaミドルウェアを作成する
    src/store/index.js
    // src/store/index.js
    import createSagaMiddleware from 'redux-saga';
    const sagaMiddleware = createSagaMiddleware();
    4.2.4サイガミドレワに登録する
    src/store/index.js
    // src/store/index.js
    createStore(reducer, applyMiddleware(sagaMiddleware))
    4.2.5 sagaを使ってaction非同期を受け入れて操作を実行する
    src/store/sagas/counter.saga.js
    // src/store/sagas/counter.saga.js
    import { takeEvery, put, delay } from 'redux-saga/effects'
    import { increment } from '../actions/counter.actions'
    import { INCREMENT_ASYNC } from '../const/counter.const'
    
    // takeEvery    action 
    // put    action 
    
    function * increment_async_fn (action) {
      yield delay(2000) //      2  
      yield put(increment(action.payload))
    }
    
    export default function * counterSaga () {
      //    action
      yield takeEvery(INCREMENT_ASYNC, increment_async_fn) //              action   
    }
    src/store/actions/counter.actions.js
    // src/store/actions/counter.actions.js
    
    //   saga   
    export const increment_async = (payload) => ({ type: INCREMENT_ASYNC, payload });
    src/store/const/counter.co nst.js
    // src/store/const/counter.const.js
    export const INCREMENT_ASYNC = 'increment_async'
    src/components/Counter.js
          
    4.2.6サガを起動する
    src/store/index.js
    // src/store/index.js
    import counterSaga from './sagas/counter.saga'
    
    sagaMiddleware.run(counterSaga);
    4.2.7合併saga
    src/store/saga/root.saga.js
    // src/store/saga/root.saga.js
    import { all } from 'redux-saga/effects'
    import counterSaga from './counter.saga'
    import modalSaga from './modal.saga'
    
    export default function * rootSaga () {
      yield all([
        counterSaga(),
        modalSaga()
      ])
    }
    modal.saga.jsは変わりません.modal.saga.jsは以下の通りです.
    src/store/saga/modal.saga.js
    // src/store/saga/modal.saga.js
    import { takeEvery, put, delay } from 'redux-saga/effects'
    import { show } from '../actions/modal.actions'
    import { SHOWMODAL_ASYNC } from '../const/modal.const'
    
    // takeEvery    action 
    // put    action 
    
    function * showModal_async_fn () {
      yield delay(2000)
      yield put(show())
    }
    
    export default function * modalSaga () {
      //    action
      yield takeEvery(SHOWMODAL_ASYNC, showModal_async_fn)
    }
    store入口ファイルのsagaミドルウェア起動root.saga
    src/store/index.js
    // src/store/index.js
    import rootSaga from './sagas/root.saga'
    
    sagaMiddleware.run(rootSaga)
    4.3 redux-actions
    4.3.1 redux-actions解決の問題
    reduxプロセスの中で大量のサンプルコードは読み书きに苦しみます.redux-actionを使ってアクションとReducerの処理を简略化できます.
    4.3.2 redux-actionのダウンロード
    npm install redux-actions
    4.3.3アクションを作成する
    import { createAction } from 'redux-actions'
    
    const increment_action = createAction('increment');
    const decrement_action = createAction('decrement');
    4.3.4 Reducerを作成する
    src/store/actions/counter.actions.js
    src/store/actions/counter.actions.js
    //    redux-actions 
    import { createAction } from 'redux-actions'
    
    export const increment = createAction('increment')
    export const decrement = createAction('decrement')
    src/store/reducers/counter.reducer.js
    // src/store/reducers/counter.reducer.js
    import { handleActions as createReducer } from 'redux-actions'
    import { increment, decrement } from '../actions/counter.actions'
    const initialState = {
      count: 0,
    }
    const handleIncrement = (state, action) => ({
      count: state.count + action.payload
    })
    
    const handleDecrement = (state, action) => ({
      count: state.count - action.payload
    })
    
    export default createReducer({
      [increment]: handleIncrement,
      [decrement]: handleDecrement,
    }, initialState)
    コンポーネント使用:src/components/Counter.js
    // src/components/Counter.js
    function Counter ({count, increment, decrement}) {
      return (
        
    {count}
    ) }
    redux-actionsはredux-sagaにも結合して使用できます.
    MobX
    1.Mobx概要
    1.1 Mobxの紹介
    簡単、拡張可能な状態管理ライブラリ
    MobxはMendix(コード開発プラットフォーム)、Coinbase(ビットコイン会社)、フェイスブックのソースと多くの個人スポンサーによって協賛されているReactとMobxのペアで、Reactはアプリケーションの状態をレンダリングし、Mobxはアプリケーションの状態を管理してReactに使用されます.
    1.2 MobXブラウザサポート
    MobX 5バージョンはES 6 Proxyをサポートするブラウザで実行されています.IE 11はサポートされていません.Node.js 6 MobX 4はES 5をサポートするブラウザで実行できます.MobX 4と5のAPIは同じです.
    2.開発前の準備
    2.1飾り器の文法サポートを有効にする(方式一)
  • 弾射項目の下の構成:npm run eject
  • 装飾器文法Babelプラグインをダウンロードする:npm install @babel/plugin-proposal-decorators
  • は、package.jsonファイルにプロファイル
  • を追加する.
    "babel": {
      "plugins": [
        [
          "@babel/plugin-proposal-decorators",
          {
            "legacy": true
          }
        ]
      ]
    }
    装飾器文法サポートを有効にします.
  • npm install react-app-rewired customize-cra @babel/plugin-proposal-decorators
  • プロジェクトのルートディレクトリの下でconfig-overrides.jsを作成し、構成
    const { override, addDecoratorsLegacy } = require("customize-cra");
    module.exports = override(addDecoratorsLegacy());
  • を追加する.
  • package.json
    "scripts": {
      "start": "react-app-rewired start",
      "build": "react-app-rewired build",
      "test": "react-app-rewired test"
    }
    2.2 vscodeエディタの飾り文法に関する警告を解決して、vscodeでcommand+カンマを押して、入力ボックスにjavascript.implicitProjectConfig.experimentalDecoratorsを入力して構成を変更します."javascript.implicitProjectConfig.experimentalDecorators": true
  • 3.Mobx+React
    3.1 Mobxをダウンロードする
    npm install mobx mobx-react
    3.2 Mobx作業フロー
    アクション->state->View
    5.Mobxデータ監視
    5.1 computed計算値
    いつ計算値を使って複雑な業務ロジックをテンプレートから抜き出しますか?
    5.2 autrun方法
    傍受の状態が変化すると、状態によって「効果」が発生しますので、atorun.atorunを使って初期化時に一回実行してください.状態が変化するたびに実行します.