1. 程式人生 > >Redux和React-Redux使用關係

Redux和React-Redux使用關係

Redux是一款狀態管理庫,並且提供了react-redux庫來與React親密配合, 但是總是傻傻分不清楚這2者提供的API和相應的關係。這篇文章就來理一理。

 

 

Redux

Redux 三大核心

Redux的核心由三部分組成:StoreActionReducer

  • Store : 是個物件,貫穿你整個應用的資料都應該儲存在這裡。
  • Action: 是個物件,必須包含type這個屬性,reducer將根據這個屬性值來對store進行相應的處理。除此之外的屬性,就是進行這個操作需要的資料。
  • Reducer
    : 是個函式。接受兩個引數:要修改的資料(state) 和 action物件。根據action.type來決定採用的操作,對state進行修改,最後返回新的state
===== Store =====
{
  todos: [],
  visibilityFilter: 'SHOW_ALL'
}


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

===== Reducer =====
const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ]
    default:
      return state
  }
}

Redux核心之間的關係

在上一部分,我們提到了,我們觸發action → reducer來處理。這就是二者之間的關係。那麼我們怎麼觸發action呢?Store這個物件提供了dispatch方法 → 觸發actiondispatch方法接受action物件作為引數。因此,我們可以瞭解三者之間的關係:

`store` ➡️ `dispatch` ➡️ `action` ⬅️ `reducer`

Store

我們通過redux提供的createStore這個方法來建立一個Store。它接受對store進行處理的reducer作為引數。

Store有三個方法:

  • getState:用來獲取store裡面儲存的資料。
  • dispatchstore裡的資料不能直接修改,只能通過觸發action來進行修改,這個方法就是用來觸發action
  • subscibe:訂閱store改變時,要進行的操作。比如在react中,當store改變時,我們需要呼叫render方法對檢視進行更新。
const store = createStore(reducer);

store.getState(); // { todos: [], visibilityFilter: 'SHOW_ALL' }

store.dispatch({
  type: 'ADD_TODO',
  text: 'Build my first Redux app'
});

store.subscibe(() => {
  console.log(store.getState());
});

Reducer

我們可以將對store的操作,寫在一個reducer中,比如:

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
    case TOGGLE_TODO:
      return Object.assign({}, state, {
        todos: todos(state.todos, action)
      })
    default:
      return state
  }
}

可以看到這個reducerstorevisibilityFiltertodos的兩部分資料進行了處理。隨著應用的複雜,如果我們把對所有資料的處理,都寫在一個reducer中,那麼它會變得很冗雜。如果我們將對每一部分的資料的處理,寫在一個單獨的reducer中,它接受該部分的資料作為state。那麼整個reducer會變得整潔和清晰。

因此,redux為我們提供了combineReducer這個API,幫助我們分開書寫reducer, 並且最終把這些reducer給集合到一個根reducer中。

// 對todos進行處理
function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    default:
      return state
  }
}

// 對 visibilityFilter 進行處理
function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

// 生成 root reducer
function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}

// 建立store
const store = createStore(todoApp)

react-redux

上一部分我們介紹了redux的核心。可以看到,redux是獨立的應用狀態管理工具。它是可以獨立於react之外的。如果我們需要在react當中運用它,那麼我們需要手動訂閱store的狀態變化,來對我們的react元件進行更新。那麼react-reudx這個工具,就幫我們實現了這個功能,我們只需對store進行處理,react元件就會有相應的變化。

這個工具主要提供兩個API

connect

現在我們有了store,那麼我們怎麼才能在我們的元件中對它們進行操作呢?connect就為提供了這個功能。它接受mapStateToPropsmapDispatchToProps等作為引數。比如在我的TodoList這個元件中需要用到todos這部分資料,那麼我完善mapStateToProps這個函式,它接受store中的state作為引數,返回一個物件,屬性就是state中我們需要的資料:

const mapStateToProps = state => {
  return {
    todos: state.todos
  }
}

mapStateToProps就將我們的state轉換為了props物件。

同樣的,我們可能需要在元件中對state進行處理。mapDispatchToProps就是幫助我們在元件中通過props呼叫dispatch來觸發action的:

const mapDispatchToProps = dispatch => {
  return {
    onTodoClick: id => {
      dispatch(toggleTodo(id))
    }
  }
}

最後我們呼叫connect這個方法,將mapStateToProps, mapDispatchToProps生成的props注入到需要使用它的組`中:

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

這樣,我們在TodoList這個元件中,就能直接通過props.todos獲取到todos中的資料, 通過props.onTodoClicktodos進行處理。

provider

上面我們呼叫connect時,在mapStateToProps 和 mapDispatchToProps我們分別用到了storestatedispatch。但是在元件中的store是哪裡憑空冒出來的呢?

provider就是來解決這個事的。Provider使它的子孫在呼叫connect方法時,都能獲取到store

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

const App = () => (
  <div>
    <AddTodo />
    <VisibleTodoList />
    <Footer />
  </div>
)

<Provider store={store}>
  <App />
</Provider>

這樣,Provider的子孫元件都能在呼叫connect時獲取到store

總結

  • Redux: storeactionreducer

    • storegetStatedispatchsubscribe
    • combineReducers
    • createStore
    • store ➡️ dispatch ➡️ action ⬅️ reducer
  • react-redux:

    • connect: 將store作為props注入
    • Provider: 使store在子孫元件的connect中能夠獲取到。