转载自 Kingsley Silas. Understanding How Reducers are Used in Redux. Oct 24, 2019
reducer是一个决定应用程序状态变化的函数。它使用它接收到的动作来确定这种变化。我们有一些工具,比如Redux,可以帮助管理应用程序在单一存储中的状态变化,使它们的行为一致。
为什么我们在谈论reducers的时候要提到Redux?Redux在很大程度上依赖于reducer函数,这些函数利用上一个状态和一个动作来执行下一个状态。
我们将在这篇文章中直接关注reducer。我们的目标是熟悉使用reducer函数,这样我们就可以看到它是如何用来更新应用程序的状态的--并最终理解它们在状态管理器中扮演的角色,比如Redux。
"状态"是什么意思
状态变化是一个基于用户的交互,甚至是类似网络请求的东西。如果应用程序的状态是由Redux管理的,那么变化就发生在reducer函数中--这是唯一发生状态变化的地方。reducer函数利用应用程序的初始状态 initial state和一些叫做动作 action的东西,来决定新的状态 new state会是什么样子。
如果我们在数学课上,我们可以说。
initial state + action = new state
从实际的reducer函数来看,是这样的。
const contactReducer = (state = initialState, action) => {
// Do something
}
我们从哪里获得初始状态和行为?这些都是我们要定义的东西
参数 state
传递给reducer函数的参数state必须是应用程序的当前状态。在这个例子中,我们把它称为initialState,因为它将是第一个(也是当前)状态,而在它之前不会有任何东西。
contactReducer(initialState, action)
比方说,我们应用的初始状态是一个空的联系人列表,我们的操作是向列表中添加一个新的联系人。
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的内容,可以查看官方文档。