createAction
用于定义 Redux action 类型和创建者的辅助函数。
function createAction(type, prepareAction?)
在 Redux 中,定义 action 的通常方法是分别声明一个action 类型常量和一个用于构造该类型 action 的action 创建者函数。
- TypeScript
- JavaScript
const INCREMENT = 'counter/increment'
function increment(amount: number) {
return {
type: INCREMENT,
payload: amount,
}
}
const action = increment(3)
// { type: 'counter/increment', payload: 3 }
const INCREMENT = 'counter/increment'
function increment(amount) {
return {
type: INCREMENT,
payload: amount,
}
}
const action = increment(3)
// { type: 'counter/increment', payload: 3 }
createAction
辅助函数将这两个声明合并为一个。它接受一个 action 类型并返回该类型的 action 创建者。action 创建者可以不带参数调用,也可以带一个要附加到 action 的payload
调用。
- TypeScript
- JavaScript
import { createAction } from '@reduxjs/toolkit'
const increment = createAction<number | undefined>('counter/increment')
let action = increment()
// { type: 'counter/increment' }
action = increment(3)
// returns { type: 'counter/increment', payload: 3 }
console.log(`The action type is: ${increment.type}`)
// 'The action type is: counter/increment'
import { createAction } from '@reduxjs/toolkit'
const increment = createAction('counter/increment')
let action = increment()
// { type: 'counter/increment' }
action = increment(3)
// returns { type: 'counter/increment', payload: 3 }
console.log(`The action type is: ${increment.type}`)
// 'The action type is: counter/increment'
使用准备回调自定义 action 内容
默认情况下,生成的 action 创建者接受一个参数,该参数将成为action.payload
。这要求调用者正确构造整个 payload 并将其传递进来。
在许多情况下,您可能希望编写额外的逻辑来自定义payload
值的创建,例如接受 action 创建者的多个参数、生成随机 ID 或获取当前时间戳。为此,createAction
接受一个可选的第二个参数:“准备回调”,它将用于构造 payload 值。
- TypeScript
- JavaScript
import { createAction, nanoid } from '@reduxjs/toolkit'
const addTodo = createAction('todos/add', function prepare(text: string) {
return {
payload: {
text,
id: nanoid(),
createdAt: new Date().toISOString(),
},
}
})
console.log(addTodo('Write more docs'))
/**
* {
* type: 'todos/add',
* payload: {
* text: 'Write more docs',
* id: '4AJvwMSWEHCchcWYga3dj',
* createdAt: '2019-10-03T07:53:36.581Z'
* }
* }
**/
import { createAction, nanoid } from '@reduxjs/toolkit'
const addTodo = createAction('todos/add', function prepare(text) {
return {
payload: {
text,
id: nanoid(),
createdAt: new Date().toISOString(),
},
}
})
console.log(addTodo('Write more docs'))
/**
* {
* type: 'todos/add',
* payload: {
* text: 'Write more docs',
* id: '4AJvwMSWEHCchcWYga3dj',
* createdAt: '2019-10-03T07:53:36.581Z'
* }
* }
**/
如果提供,action 创建者的所有参数都将传递给准备回调,它应该返回一个包含payload
字段的对象(否则创建的 action 的 payload 将为undefined
)。此外,该对象可以包含一个meta
和/或一个error
字段,它们也将添加到创建的 action 中。meta
可能包含有关 action 的额外信息,error
可能包含有关 action 失败的详细信息。这三个字段(payload
、meta
和error
)符合Flux Standard Actions的规范。
注意:类型字段将自动添加。
与 createReducer() 一起使用
action 创建者可以直接传递到createReducer()构建回调中的addCase
中。
- TypeScript
- JavaScript
import { createAction, createReducer } from '@reduxjs/toolkit'
const increment = createAction<number>('counter/increment')
const decrement = createAction<number>('counter/decrement')
const counterReducer = createReducer(0, (builder) => {
builder.addCase(increment, (state, action) => state + action.payload)
builder.addCase(decrement, (state, action) => state - action.payload)
})
import { createAction, createReducer } from '@reduxjs/toolkit'
const increment = createAction('counter/increment')
const decrement = createAction('counter/decrement')
const counterReducer = createReducer(0, (builder) => {
builder.addCase(increment, (state, action) => state + action.payload)
builder.addCase(decrement, (state, action) => state - action.payload)
})
从 Redux 5.0 开始,动作类型 *必须* 是字符串。如果非字符串动作类型到达原始商店调度,商店将抛出错误。
actionCreator.match
每个生成的 actionCreator 都有一个 .match(action)
方法,可用于确定传递的动作是否与 actionCreator 创建的动作类型相同。
这有不同的用途
作为 TypeScript 类型守卫
此 match
方法是 TypeScript 类型守卫,可用于区分动作的 payload
类型。
这种行为在自定义中间件中特别有用,否则可能需要手动转换。
- TypeScript
- JavaScript
import { createAction } from '@reduxjs/toolkit'
import type { Action } from '@reduxjs/toolkit'
const increment = createAction<number>('INCREMENT')
function someFunction(action: Action) {
// accessing action.payload would result in an error here
if (increment.match(action)) {
// action.payload can be used as `number` here
}
}
import { createAction } from '@reduxjs/toolkit'
const increment = createAction('INCREMENT')
function someFunction(action) {
// accessing action.payload would result in an error here
if (increment.match(action)) {
// action.payload can be used as `number` here
}
}
使用 redux-observable
match
方法也可以用作过滤方法,这使得它在与 redux-observable 一起使用时非常强大。
- TypeScript
- JavaScript
import { createAction } from '@reduxjs/toolkit'
import type { Action } from '@reduxjs/toolkit'
import type { Observable } from 'rxjs'
import { map, filter } from 'rxjs/operators'
const increment = createAction<number>('INCREMENT')
export const epic = (actions$: Observable<Action>) =>
actions$.pipe(
filter(increment.match),
map((action) => {
// action.payload can be safely used as number here (and will also be correctly inferred by TypeScript)
// ...
})
)
import { createAction } from '@reduxjs/toolkit'
import { map, filter } from 'rxjs/operators'
const increment = createAction('INCREMENT')
export const epic = (actions$) =>
actions$.pipe(
filter(increment.match),
map((action) => {
// action.payload can be safely used as number here (and will also be correctly inferred by TypeScript)
// ...
})
)