淺顯易懂的Redux深究

LinSWHS 8年前發布 | 58K 次閱讀 Redux JavaScript開發

來自: http://galen-yip.com/react/2016/02/06/go-into-redux/

半個多月redux的學習,分析下redux,當做記錄吧

題外話,有看到前些天轉的很火的 @徐叔叔的回答 。說下就自己的看法吧,這也是為什么有這篇redux研究文章的原因。考慮適用性,團隊效率等等都是十分贊同的,但就個人而言,自己純屬是為了滿足個人的好奇心,為了在這大浪淘沙的前端變革時代,能身為一個歷經者。僅此而已。

</div>

核心:

  • 只使用一個store將整個應用程序的狀態state用object tree的方式存儲。原生的flux會有許多分散的store存儲各自不同的狀態,但在redux中,只有一個store。
  • </ul>

    //原生 Flux 的 store
    var firstStore = {
        first: 1
    }
    var secondStore = {
        second: 2
    }

    // Redux 的單一 store var state = { firstState: { first: 1 }, secondState: { second: 2 } }</pre></div>

    • 單向數據流,統一入口,改變state只能發送action,action就是一個東西,告訴state該怎么改變而已。這個特性跟原生的flux是一樣的。

      </li>

    • 接受action,更改state的叫做Reducer。Reducer包含兩個參數,舊的state和action,返回新的state

      </li> </ul>

      Actions

      在redux和flux中,actions都是一個告知state要變更的東西 形如:

      {
        type: ADD_TODO,
        payload: {
          text: 'Build my first Redux app'
        }
      }

      Actions Creators

      在flux中,action creator要做dispatch的動作 redux中,action creator只是簡單的回傳action

      flux中:

      function addTodoWithDispatch(text) {
          dispatch({
              type: ADD_TODO,
              payload: {
                  text
              }
          });
      }
      // 實際發送 action
      addTodoWithDispatch(text)

      redux中:

      function addTodo(text) {
          return {
              type: ADD_TODO,
              payload: {
                  text
              }
          };
      }

      // 實際發送 action dispatch(addTodo(text)); dispatch(removeTodo(id));</pre></div>

      在redux中,dispatch()方法來源于store.dispatch() 但react-redux中提供了connnector元件,這個元件會把dispatch提取出來給我們使用,所以不需要我們從store中提取

      redux中提供了bindActionCreators(),就是將ActionCreator外加上了一層dispatch(),因為都想偷懶不寫dispatch(removeTodo(id))這些綁定代碼

      所以bindActionCreator()是常用的方法之一

      </div>

      Reducers

      Reducers類似于flux里面的Store,但在Redux中,它是擋在Store前的一個東西,Redux里只有一個Store,所以這些Reduers的功能就是針對這個唯一的Store內的State的部分內容進行更新 Reduer接收2個參數,舊的State和action,返回新的State 例子:

      const initialState = { todos: [], idCounter: 0 };

      function todos(state = initialState, action) { switch (action.type) { case ADD_TODO: return { ...state, todos: [ ...state.todos, { text: action.payload, id: state.idCounter + 1 } ], idCounter: state.idCounter + 1 }; case REMOVE_TODO: return { ...state, todos: state.todos.filter(todo => todo.id !== action.payload) }; default: return state; } }</pre></div>

      …state就是展開操作符,這里這么用的作用是返回新的對象,不修改原來的對象

      Store

      Redux中只有一個Store,這個Store是基于許多的Reduers上的 Redux提供了createStore()方法

      如果是只有單個Reducer時:

      import { createStore } from 'redux';
      import todos from '../reducers/todos';
      const store = createStore(todos);

      多個Reducer時,則用combineReducers()把多個reducers合并成一個再丟給createStore()

       
      export function todos(state, action) {
        / ... /
      }

      export function counter(state, action) { / ... / }

      import { createStore, combineReducers } from 'redux'; import * as reducers from '../reducers'; const reducer = combineReducers(reducers); const store = createStore(reducer);</pre></div>

      得出的Store是這樣的:

      {
          dispatch,
          subscribe,
          getState,
          getReducer,
          replaceReducer
      };

      調用getState(),可以得到state

      const state = store.getState();
      console.log(state);

      // 結果: { todos: todoState, counter: counterState }</pre></div>

      元件Container

      官方使用的是container pattern,即有一個只管接收props并render的“笨”元件,和一個包括在笨元件外面的負責管理和傳遞數據的container元件 container元件也就負責與Redux交流

      react-redux提供了 Provider元件和connect方法

      Provider

      用在應用的根元素外面,負責傳遞唯一的Store給應用

      const reducer = combineReducers(reducers);
      const store = createStore(reducer);

      class App extends Component { render() { return ( <Provider store={store}> {() => <App />} </Provider> ); } }</pre></div>

      connect

      用法: connect(select)(Component) connect接收一個函數當參數并傳回一個Component Class

      作用:將dispatch方法透過props的方式加到元件中,而且還能選取這個container需要state的哪一部分 例如下面,只選取state里的counter部分

      function select(state) {
        return { counter: state.counter };
      }

      class CounterApp { render() { const { counter, dispatch } = this.props; return ( <Counter counter={counter} /> ); } }

      export default connect(select)(CounterApp)</pre></div>

      當然,也可以不加select方法,這樣Connector收到的就是整個應用程序完整的

      下面是很好的完整的redux的demo:

      import React from 'react';
      import { createStore } from 'redux';
      import { Provider, connect } from 'react-redux';

      // React component class Counter extends React.Component { render(){ const { value, onIncreaseClick } = this.props; return ( <div> <span>{value}</span> <button onClick={onIncreaseClick}>Increase</button> </div> ); } }

      // Action: const increaseAction = {type: 'increase'};

      // Reducer: function counter(state={count: 0}, action) { let count = state.count; switch(action.type){ case 'increase': return {count: count+1}; default: return state; } }

      // Store: let store = createStore(counter);

      // Map Redux state to component props function mapStateToProps(state) { return { value: state.count }; }

      // Map Redux actions to component props function mapDispatchToProps(dispatch) { return { onIncreaseClick: () => dispatch(increaseAction) }; }

      // Connected Component: let App = connect( mapStateToProps, mapDispatchToProps )(Counter);

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

      自己理解的結構圖

      中間件

      redux本身能很好的處理同步的情況,但異步的情況在web開發中必不可少。 我把同步和異步分開來說

      同步:

      通過redux暴露出的applyMiddlewares,我們可以得到一個新的dispatch. 所以applyMiddlewares的實際效果可以看做是: dispatch => newdispatch

      異步:

      對于異步的情況,我們拿redux-thunk為例,它會對傳入的action進行判斷,如果是function的話。則去把我們的action執行,這個執行的就是我們的delay代碼。然后在最里面執行dispatch()。所以我們的箭頭又重新回到最上面,這一次就相當于同步的使用了

      實踐中

      下面是實際開發中使用到的

      項目目錄

      |-- actions
      |-- components
      |-- constants
      |-- containers
      |-- index.jsx
      |-- main.css
      |-- middleware
      |-- reducers
      |-- routes
      |-- store

      • components 是沒有關于actions和stores的依賴,拿出項目也可以單獨使用
      • </ul>

        項目中會用到的工具類(中間件):

        • redux-thunk
        • </ul>

          使得dispatch給store時是異步的,比如action中需要ajax

          • redux-logger
          • </ul>

            記錄狀態改變前和后

            • immutable
            • </ul>

              不可變數據,即對于復雜結構的對象,如果需要改變某一層某個值,不像過去進行一次深拷貝那樣浪費資源,而是只改變該層以上的一些引用,其他沒變的仍然指針指向原來的地址

              在后期性能優化中,對于嵌套組件而言,希望沒改變的不重新渲染。直接對比this.state和previous.state是否全等則可以,因為如果有修改,則最頂層的引用是修改了的

              • react-immutable-render-mixin
              • </ul>

                用ES7 decorator的方式引入 用于跳過更新沒有改變state的子組件 代替了 pure-render-mixim的功能

                • redux-devtools
                • </ul>

                  3.0.1現在的版本是,改變比較大,參考他的github 如果是babel6+ 有些地方需要加上 .default

                  下面是開發中遇到的幾個注意點:

                  一、 注意這里的items和filter,這些就是state!!!

                  export default combineReducers({
                      items,
                      filter
                  })

                  二、 用了es6,則mixant的用法不可用,用HOC(高階組件)替代

                  關于HOC和mixing、decoratorwww.jianshu.com

                  文章有點流水了,其中如有不當的還請指出,或者大家交流交流~~~

                  </div>

                  • ← Previous Post
                  • </ul> </div> </code></code></code></code></code></code></code></code></code></code></code></code></code></code>

                   本文由用戶 LinSWHS 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
                   轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
                   本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!