我认为你的方向是正确的。
正如您所说,您可以在您的操作中添加一个新字段,其中放置您需要记录的额外信息。例如,如果您查看 FSA (Flux Standard Action),它允许属性 meta 用于此目的。
可选的元属性可以是任何类型的值。它适用于不属于有效负载的任何额外信息。
要记录您的操作,一个好的解决方案是使用一个或多个专用的redux-middleware 负责此操作,以捕获具有特殊“特征”的每个操作。
例如,您可以使用符号(例如 LOG)标记动作并使用中间件轻松捕获它,或者,您可以捕获呈现属性 meta 的动作。
受real-world redux repo 中的示例启发的小sn-p:
// LOG symbol
export const LOG = Symbol('LOG')
// action
import { LOG } from './....'
export function moveForward () {
return {
[LOG]: {
type: "MOVED_FORWARD_X_PAGES,
numberOfPages: 5,
nowShowingPage: 53 // <== log file would like this additional info
}
}
// A Redux middleware that interprets actions with LOG info specified.
import { LOG } from './....'
export default store => next => action => {
const log = action[LOG]
if (typeof log === 'undefined') {
return next(action)
}
// here perform log
// ...
}
看看这个库redux-segment(redux 的分析集成),它使用属性 meta 告诉中间件记录操作。
希望对你有帮助。
更新
我尝试在下面回答您的问题。
redux 的一个很好的模式是对动作(和组件)隐藏状态形状。所以通过你需要登录的项目的路径并不是什么大问题。
说了这么多,不知道有没有简单优雅的解决你的问题,不过我尽量给你一些思路:)
例如,您可以在操作中设置 selector 的名称,而不是 路径,您想使用它来获取要记录的信息。
这样安排:
- 您将动作与状态解耦,因此将来您可以在不更改动作的情况下更改状态形状,而只需更改选择器
- 您可以使用reselect 并编写更多选择器来记录更多信息
- 你不需要使用eval
- 你需要为每一个需要记录的信息设置一个选择器
一些代码(未经测试)可以更好地解释我的意思。
// action moveForward.js
export function moveForward () {
return {
type: "MOVED_FORWARD_X_PAGES",
payload: { numberOfPages: 5, nowShowingPage: 53 }
meta: { selector: 'getShowedPage' }
}
}
// action selectItem.js
export function selectItem () {
return {
type: "SELECTED_ITEM_FROM_LIST",
payload: { index: 4 }
meta: { selector: 'getSelectedItem' }
}
}
// reducer pages.js
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case "MOVED_FORWARD_X_PAGES":
return Object.assign({}, state, {
numberOfPages: action.payload.numberOfPages,
nowShowingPage: action.payload.nowShowingPage,
// ...
})
}
// reducer items.js
const initialState = { list: [], currentItem: undefined }
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case "SELECTED_ITEM_FROM_LIST":
return Object.assign({}, state, {
currentItem: action.payload.index,
// ...
})
}
// log_middleware.js
import logger from './logger'
export default store => next => action => {
// if action not contains extra info to log (meta) go ahead
if (!action.meta)
return next(action)
// Save current state and get the next one
const prevState = store.getState()
const returnedValue = next(nextAction)
const nextState = store.getState()
// Perform log
logger(prevState, nextState, action)
// return the next state
return returnedValue
}
// selectors.js
const getShowedPage = state => state.nowShowingPage
const getSelectedItem = state => state.list[state.currentItem]"
export default selectors = {
getShowedPage,
getSelectedItem
}
// logger.js
import selectors from '../selectors'
export default logger (prevState, nextState, action) {
const selector = state => selectors[action.meta.selector]
const infoToLog = selector(nextState)
// Perform log
console.log(infoToLog)
}
要编写更多选择器,您可以使用reselect。
最后看一下 D. Abramov 的 this 选择器视频教程和 redux-logger 源代码。