【譯】Redux 中的Reducer 函數

轉載自 Kingsley Silas. Understanding How Reducers are Used in Redux. Oct 24, 2019css

reducer是一個決定應用程序狀態變化函數。它使用它接收到的動做來肯定這種變化。咱們有一些工具,好比Redux,能夠幫助管理應用程序在單一存儲中的狀態變化,使它們的行爲一致。redux

爲何咱們在談論reducers的時候要提到Redux?Redux在很大程度上依賴於reducer函數,這些函數利用上一個狀態和一個動做來執行下一個狀態。markdown

咱們將在這篇文章中直接關注reducer。咱們的目標是熟悉使用reducer函數,這樣咱們就能夠看到它是如何用來更新應用程序的狀態的--並最終理解它們在狀態管理器中扮演的角色,好比Redux。網絡

"狀態"是什麼意思

狀態變化是一個基於用戶的交互,甚至是相似網絡請求的東西。若是應用程序的狀態是由Redux管理的,那麼變化就發生在reducer函數中--這是惟一發生狀態變化的地方。reducer函數利用應用程序的初始狀態 initial state和一些叫作動做 action的東西,來決定新的狀態 new state會是什麼樣子。app

若是咱們在數學課上,咱們能夠說。ide

initial state + action = new state
複製代碼

從實際的reducer函數來看,是這樣的。函數

const contactReducer = (state = initialState, action) => {
  // Do something
}
複製代碼

咱們從哪裏得到初始狀態和行爲?這些都是咱們要定義的東西工具

參數 state

傳遞給reducer函數的參數state必須是應用程序的當前狀態。在這個例子中,咱們把它稱爲initialState,由於它將是第一個(也是當前)狀態,而在它以前不會有任何東西。oop

contactReducer(initialState, action)
複製代碼

比方說,咱們應用的初始狀態是一個空的聯繫人列表,咱們的操做是向列表中添加一個新的聯繫人。ui

const initialState = {
  contacts: []
}
複製代碼

這就建立了咱們的InitialState,它等於咱們在reducer數中須要的參數state

參數 action

一個action是一個包含兩個鍵及其值的對象。在reducer中發生的狀態更新老是依賴於action.type的值。在這個場景中,咱們要演示的是當用戶試圖建立一個新的聯繫人時發生的事情。因此,讓咱們把action.type定義爲NEW_CONTACT

const action = {
  type: 'NEW_CONTACT',
  name: 'John Doe',
  location: 'Lagos Nigeria',
  email: 'johndoe@example.com'
}
複製代碼

一般有一個payload值,包含用戶發送的內容,將用於更新應用程序的狀態。須要注意的是,action.type 是必需的,但 action.payload 是可選的。使用payload爲action對象的外觀帶來了必定程度的結構性。

更新狀態

狀態的意思是,它是不可變的,也就是說不該該直接改變它。要建立一個更新的狀態,咱們能夠利用Object.assign或者選擇spread操做符

Object.assign

const contactReducer = (state, action) => {
  switch (action.type) {
    case 'NEW_CONTACT':
    return Object.assign({}, state, {
      contacts: [
        ...state.contacts,
        action.payload
      ]
    })
    default:
      return state
  }
}
複製代碼

在上面的例子中,咱們使用了Object.assign()來確保咱們不直接改變狀態值。相反,它容許咱們返回一個新的對象,這個對象充滿了傳遞給它的狀態和用戶發送的有效載荷。

要使用Object.assign(),重要的是第一個參數是一個空對象。傳遞狀態做爲第一個參數會致使它被突變,這也是咱們要避免的,以保持一致性。

spread 操做符

object.assign()的替代方法是使用spread操做符,像這樣。

const contactReducer = (state, action) => {
  switch (action.type) {
    case 'NEW_CONTACT':
    return {
        ...state, contacts:
        [...state.contacts, action.payload]
    }
    default:
      return state
  }
}
複製代碼

這確保了在咱們將新項目追加到底部時,入庫狀態保持不變。

使用switch語句

前面,咱們注意到,發生的更新取決於action.type的值。switch語句根據action.type的值,有條件地決定咱們要處理的更新類型。

這意味着一個典型的reducer會是這樣的。

const addContact = (state, action) => {
  switch (action.type) {
    case 'NEW_CONTACT':
    return {
        ...state, contacts:
        [...state.contacts, action.payload]
    }
    case 'UPDATE_CONTACT':
      return {
        // Handle contact update
      }
    case 'DELETE_CONTACT':
      return {
        // Handle contact delete
      }
    case 'EMPTY_CONTACT_LIST':
      return {
        // Handle contact list
      }
    default:
      return state
  }
}
複製代碼

重要的是,當action對象中指定的 "action.type "的值與reducer中的值不匹配時,咱們要返回咱們的 "default "狀態--好比說,若是因爲某些未知的緣由,action看起來像這樣。

const action = {
  type: 'UPDATE_USER_AGE',
  payload: {
    age: 19
  }
}
複製代碼

因爲咱們沒有這種動做類型,因此咱們但願返回狀態中的內容(應用程序的當前狀態)來代替。總之咱們不肯定用戶此刻想達到什麼目的。

把全部東西放在一塊兒

下面是我在React中實現reducer函數的一個簡單例子。

const initialState = {
  contacts: [{
    name: 'Vic Henry',
    age: 30
  }]
};

const contactReducer = (state = initialState, action) => {
  switch (action.type) {
    case "NEW_CONTACT":
      return Object.assign({}, state, {
        contacts: [...state.contacts, action.payload]
      });
    default:
      return state;
  }
};

class App extends React.Component {
  constructor(props) {
    super(props);
    this.name = React.createRef();
    this.age = React.createRef();

    this.state = initialState;
  }

  handleSubmit = e => {
    e.preventDefault();
    const action = {
      type: "NEW_CONTACT",
      payload: {
        name: this.name.current.value,
        age: this.age.current.value
      }
    };
    const newState = contactReducer(this.state, action);
    this.setState(newState);
  };

  render() {
    const { contacts } = this.state;
    return (
      <div className="box"> <div className="content"> <pre>{JSON.stringify(this.state, null, 2)}</pre> </div> <div className="field"> <form onSubmit={this.handleSubmit}> <div className="control"> <input className="input" placeholder="Full Name" type="text" ref={this.name} /> </div> <div className="control"> <input className="input" placeholder="Age" type="number" ref={this.age} /> </div> <div> <button type="submit" className="button">Submit</button> </div> </form> </div> </div>
    );
  }
}


ReactDOM.render(
  <App />,
  document.getElementById('root')
);
複製代碼

你能夠看到,我沒有使用Redux,但這與Redux使用reducer來存儲和更新狀態變化的方式很是相同。主要的狀態更新發生在reducer函數中,它返回的值設置了應用程序的更新狀態。

想試一試嗎?你能夠擴展reducer函數,讓用戶更新聯繫人的年齡。我想在評論區看看你想到了什麼!

瞭解Redux中reducer所扮演的角色,應該能讓你更好地理解引擎下面發生的事情。若是你有興趣閱讀更多關於在Redux中使用reducer的內容,能夠查看官方文檔

相關文章
相關標籤/搜索