轉載自 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
}
複製代碼
咱們從哪裏得到初始狀態和行爲?這些都是咱們要定義的東西工具
傳遞給reducer函數的參數state
必須是應用程序的當前狀態。在這個例子中,咱們把它稱爲initialState
,由於它將是第一個(也是當前)狀態,而在它以前不會有任何東西。oop
contactReducer(initialState, action)
複製代碼
比方說,咱們應用的初始狀態是一個空的聯繫人列表,咱們的操做是向列表中添加一個新的聯繫人。ui
const initialState = {
contacts: []
}
複製代碼
這就建立了咱們的InitialState
,它等於咱們在reducer數中須要的參數state
。
一個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操做符。
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()
,重要的是第一個參數是一個空對象。傳遞狀態做爲第一個參數會致使它被突變,這也是咱們要避免的,以保持一致性。
object.assign()
的替代方法是使用spread操做符,像這樣。
const contactReducer = (state, action) => {
switch (action.type) {
case 'NEW_CONTACT':
return {
...state, contacts:
[...state.contacts, action.payload]
}
default:
return state
}
}
複製代碼
這確保了在咱們將新項目追加到底部時,入庫狀態保持不變。
前面,咱們注意到,發生的更新取決於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的內容,能夠查看官方文檔。