如何在 React-Redux 中将 state sanitizer 与现有的中间件结合起来

Posted

技术标签:

【中文标题】如何在 React-Redux 中将 state sanitizer 与现有的中间件结合起来【英文标题】:How to combine state sanitizer with existing middleware in React-Redux 【发布时间】:2020-05-06 16:03:52 【问题描述】:

我的 redux 商店相当大; Redux Devtools 建议清理较大的对象以提高性能。

我在这里关注了文档:https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Troubleshooting.md#excessive-use-of-memory-and-cpu

我在这里尝试了许多组合,但没有一个能提供我期望的输出。

当前版本(如下所示)导致状态作为函数返回,而不是对象。我知道我做错了什么,但我不确定是什么。任何指导将不胜感激。

这是我的 store.js:


    'use strict'
    // libraries
    import  createStore, applyMiddleware, compose  from 'redux'

    // middleware
    import logger from 'redux-logger'
    import thunk from 'redux-thunk'

    // reducers
    import reducer from './reducers'

    const withLogger = false ? (thunk, logger) : thunk

    const isProd = process.env.NODE_ENV === 'production'

    const middleware = isProd ? thunk : withLogger

    const composeEnhancers = isProd
      ? compose
      : window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

    // sanitizers to keep redux devtools from using excessive memory
    const actionSanitizer = action =>
      !!action.id
      && action.type === `RECEIVE_$action.id.toString().toUpperCase()_COLLECTION`
        ?  ...action, data: '<<LONG_BLOB>>' 
        : action

    const store = createStore(
      reducer,
      composeEnhancers(applyMiddleware(middleware)),


// The addition of this code breaks my store

      window.__REDUX_DEVTOOLS_EXTENSION__
        && window.__REDUX_DEVTOOLS_EXTENSION__(
          actionSanitizer,
          stateSanitizer: state =>
            state.data ?  ...state, data: '<<LONG_BLOB>>'  : state
        )

// End breaking code

    )

第二次尝试

我进行了几次更新,现在可以在 devtools 中看到消毒剂的效果 - 取决于我在 createStore 函数中的位置。不幸的是,这改变了我的 composeEnhancers 行为(根据位置触发或不触发)


// middleware with or without logger
const middlewareEnhancer =
  true || ENV === 'production' // change to false to prevent logger output
    ? applyMiddleware(thunk, logger)
    : applyMiddleware(thunk)

// sanitizers to keep redux devtools from using excessive memory
const actionSanitizer = action =>
  !!action.id
  && action.type === `RECEIVE_$action.id.toString().toUpperCase()_COLLECTION`
    ?  ...action, data: '<<LONG_BLOB>>' 
    : action

// compose
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__(middlewareEnhancer) ||
  compose(middlewareEnhancer)

const store = createStore(
  // createStore signature > reducer, preLoadedState, enhancer
  rootReducer,
  // devtools extension works when I place it here per the examples in docs
  // BUT composed enhancers fail
  // Obviously, since the format wouldn't match the createStore signature
  // I have no idea how `__REDUX_DEVTOOLS_EXTENSION__` should be used in conjunction with composeEnhancers

  undefined,
  composeEnhancers,

  // devtools extension fails when placed here
  // composed enhancers run

  window.__REDUX_DEVTOOLS_EXTENSION__
    && window.__REDUX_DEVTOOLS_EXTENSION__(
      actionSanitizer,
      stateSanitizer: state =>
        state.data ?  ...state, data: '<<LONG_BLOB>>'  : state
    )

)

最后,坚持ftw!

我讨厌放弃;在重新阅读@markerikson 发布的所有文档后发现了这一点。总是阅读文档:'(

这可能对使用configureStoreRedux Toolkit 的任何人都没有用,但无论如何我都会记录下来。

我的大错误是 actionSanitizerstateSanitizer 是 Devtools 扩展选项,应该这样添加。觉得自己很傻,但至少我不会忘记。

剩下要做的唯一事情是实现redux-devtools-extension 以避免使用markerikson 建议的window.__SOMEFUNC__

实际解决方案:

'use strict'
// libraries
import  createStore, applyMiddleware, compose  from 'redux'

// middleware
import logger from 'redux-logger'
import thunk from 'redux-thunk'

// reducers
import rootReducer from './reducers'

// middleware with or without logger
const middlewareEnhancer =
  true || ENV === 'production' // change to false to prevent logger output
    ? applyMiddleware(thunk, logger)
    : applyMiddleware(thunk)

// sanitizers to keep redux devtools from using excessive memory
const actionSanitizer = action =>
  !!action.id
  && action.type === `RECEIVE_$action.id.toString().toUpperCase()_COLLECTION`
    ?  ...action, data: '<<LONG_BLOB>>' 
    : action

// compose
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__(

    // add sanitizers here as devtools options
    // see https://github.com/zalmoxisus/redux-devtools-extension/tree/94f7e53800f4665bddc9b7438c5cc75cfb4547cc#12-advanced-store-setup
    // section 1.2

    actionSanitizer,
    stateSanitizer: state =>
      state.data ?  ...state, data: '<<LONG_BLOB>>'  : state
  ) || compose

const enhancer = composeEnhancers(middlewareEnhancer)

const store = createStore(rootReducer, undefined, enhancer)

export default store

【问题讨论】:

【参考方案1】:

作为第一个观察,这条线似乎是错误的:

const withLogger = false ? (thunk, logger) : thunk

我强烈建议您首先切换到使用 the configureStore function from our official Redux Toolkit package,它会为您处理商店设置过程。如果需要,您仍然可以从那里将 DevTools 配置选项传递给 configureStore()

【讨论】:

我使用 withLogger 来隐藏/显示记录器中间件的控制台输出。我通常把它留给false,因为记录器在控制台中添加了太多噪音。这个设置在不包含额外的状态清理器的情况下工作正常,所以 withLogger 变量不会影响我的预期输出(不过我会看看工具包,它看起来确实很有帮助) 我是说三元语句在当前编写时看起来是错误的,尤其是 (thunk, logger) 部分。至少,我还建议通读 Redux 文档中的 Configuring Your Store 页面以获取商店设置示例。 看来我落后了。我有一段时间没有阅读文档了。谢谢你的帮助。我会回来更新我的帖子。 我在 Redux 4x 文档中没有看到任何关于 actionSanitizers 的提及。我认为它不再需要。看来要更新了。 DevTools 扩展文档show where to add configuration options for "manual" setup logic under the "Advanced Store Setup" heading。但是,正如我已经说过的,DevTools 扩展中显示的“手动”window.WHATEVER 逻辑很难使用。请对该部分使用redux-devtools-extension NPM 包或 Redux Toolkit,如Configuring Your Store 所示。然后传入选项。【参考方案2】:

仅为那些使用 redux toolkit 的人完成答案,这里有一个适合我的示例条目。

  actionSanitizer: (action) => 

    switch (true) 

      case action.type.includes(RESOLVED):
        return typeof action.payload !== 'undefined'
          ?  ...action, payload: '<<LONG_BLOB>>' 
          :  ...action, results: '<<LONG_BLOB>>' ;
      
      /* ... more entries */

      default:
        return action;
    
  ,

  stateSanitizer: (state) =>
    state.data?.matrix
      ?  ...state, data:  ...state.data, matrix: '<<LONG_BLOB>>'  
      : state,

;

然后我在工具包的configureStore函数中引用配置:

  const store = configureStore(
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) =>
      getDefaultMiddleware(
        thunk: false,
        serializableCheck: false,
        immutableCheck: false,
      ).prepend(middlewares),
    preloadedState: initialState,
    devTools: devToolsConfiguration,  // <<< here
  );

【讨论】:

以上是关于如何在 React-Redux 中将 state sanitizer 与现有的中间件结合起来的主要内容,如果未能解决你的问题,请参考以下文章

react-redux笔记

react-redux笔记

react-redux笔记

react-redux笔记

react 中的 redux 和react-redux的区别分析

关于react-redux的mapStateToProps取到值(state会更新变化)但不会注入props的问题