我应该将函数引用存储在 Redux 存储中吗?

Posted

技术标签:

【中文标题】我应该将函数引用存储在 Redux 存储中吗?【英文标题】:Should I store function references in Redux store? 【发布时间】:2016-05-21 09:28:22 【问题描述】:

我正在尝试以惯用的 React/Redux 方式键盘快捷键支持构建到我的 React/Redux 应用程序中。我计划这样做的方式是拥有以下动作创建者和相关动作:

registerShortcut(keyCode, actionCreatorFuncReference)

然后,reducer 将使用 keyCodes 到 actionCreatorFuncReferences 的映射更新 redux 存储中的 registeredShortcuts 对象。然后我的根组件将侦听keyup 并查看是否有关联的keyCode 注册,如果有,则通过动作创建器函数引用调度映射的动作。

不过,这将是我第一次在我的 Redux 存储中存储函数引用。迄今为止,我只有带有原始值(字符串、整数等)的键的对象。

Redux 文档说:

您应该尽最大努力保持状态可序列化。不要在里面放任何你不能轻易转换成 JSON 的东西。

这是否表明在我的 Redux 存储中存储此类函数引用是一个坏主意?如果是这样,有什么更好的方法来完成我在 React/Redux 中尝试做的事情?

另一种方法是将 keyCodes 的映射和函数引用存储在根 react 组件本身中,但这感觉不太像 Redux,因为现在应用程序状态不在 Redux 存储中。

【问题讨论】:

我自己也一直在想这种事情。虽然 Redux 状态肯定需要可序列化,但有时您可能还想保留其他一些东西(承诺等)。我刚刚在github.com/rackt/redux/issues/1385 中提出了这个问题 - 我们会看看是否有任何问题。 【参考方案1】:

不,您不应该在 redux 存储中存储函数引用。它们不可序列化,正如您提到的,状态应该始终是可序列化的。我能想到的对 redux 最友好的方法就是将热键映射到他们的actionCreatorFuncNames

【讨论】:

这是一个有趣的想法。然后我将如何从 actionCreatorFuncNames 字符串中实际执行该函数? 我认为这将取决于您的应用程序的详细信息。无论您在哪里处理这些关键事件,都可以轻松地将所有操作导入到单个 actions 对象中。困难的部分是将按钮按下并使用正确的参数调用actions[actionCreatorFuncNameA]。另一方面,如果热键都是切换,那么你不需要调用任何参数,问题就解决了。这有意义吗,还是我不在基地? 在您的商店中存储函数很好 - 只要您了解所涉及的权衡。来自常见问题解答:“如果您对持久性和时间旅行调试等可能无法按预期工作的事情感到满意,那么完全欢迎您将不可序列化的项目放入您的 Redux 存储中。” redux.js.org/docs/faq/… 谁说 redux store 应该是可序列化的?除非您进行服务器渲染,否则我认为避免它没有任何价值。 看起来 Redux 在这个问题上改变了主意:redux.js.org/style-guide/… 不推荐使用不可序列化的项目,除非它们打算被中间件拦截。【参考方案2】:

你没有。存储状态必须始终可序列化(如Nathananswered)。 Redux 方式是通过enhancers,或者 Redux-Observable 方式是通过dependencies。

基于 Redux 文档 example,您想要的是在您的操作 (1) 中传递引用,在您的 reducer (2) 中忽略它并在您的增强器 (3) 中使用它:

//... in your action:
const data=val:1, ref=()=>;
const action = type:'ACTION_WITH_REF', data, ref; //(1)

//... in your reducer:
   case 'ACTION_WITH_REF':
   return ...state, data: action.data; //(2)

//... and in your enhancer:
import  createStore, applyMiddleware  from 'redux';
import reducers from './reducers';
export const myRefStore= ;
 
function refHandler( getState ) 
  return next => action => 
    switch(action.type) 
       // this can be done more elegantly with a redux-observable
       case 'ACTION_WITH_REF':
         myRefStore.aRef = action.ref; // (3)
         break;
     
    // be sure to maintain the chain of the store
    const returnValue = next(action);   
    // otherwise, your midddeware will break the store
    return returnValue;
  ;
 

const store = createStore(
  reducers,
  initialState,
  applyMiddleware(refHandler)
);

注意:只要您的增强剂没有副作用,您就可以开始了。请注意,您可以直接在 reducer 中获取 refs,但这种方法将引用保留在 reducer 级别,并错过了combineReducers() 的要点。使用增强器,您可以将它们全部保存在一个位置 (myRefStore)。

最后一个观察结果是,redux 存储不是任何数据存储,而是状态存储,因此我们需要在增强器中处理函数和其他与状态无关的东西。您可以将增强器主干用于Redux-Observable 并通过dependencies 注入myRefStore

【讨论】:

您的回答非常好,应该是公认的解决方案!由于您的示例,我能够以非常复杂的结构存储函数;)【参考方案3】:

我是 redux 的新手,但在我看来,您可以传递关键代码和操作类型。 然后,reducer 可能会监听该动作类型并相应地进行更改。

这是一个使用 Mousetrap 库的示例:

// On your Container
function registerShortcut(element, dispatch, keyCode, actionType) 
  Mousetrap(element).bind(keyCode, function(e) 
    dispatch(
      type: actionType,
      payload: 
        keyCode: keyCode,
        event: e
      
    );
  );
);

mapDispatchToProps = function(dispatch) 
  return 
    onMount: function(element) 
      registerShortcut(element, dispatch, ['command+f', 'ctrl+f'], 'OPEN_SEARCH');
    ,
    onUnmount: function(element) 
      Mousetrap(element).unbind(['command+f', 'ctrl+f']);
    
  ;
;


// On your Component
componentDidMount() 
  onMount(ReactDOM.findDOMNode(this));
;

componentWillUnmount() 
  onUnmount(ReactDOM.findDOMNode(this));
;



// On your reducer
function reducer(oldState, action)  
  if (action.type == 'OPEN_SEARCH') 
    //... make changes ...//
    return newState;
  
  return oldState;
;

这样,键盘快捷键将调度一个动作。 reducer 将对状态进行必要的更改。最后,应用程序可以重新渲染。

【讨论】:

以上是关于我应该将函数引用存储在 Redux 存储中吗?的主要内容,如果未能解决你的问题,请参考以下文章

我可以将 JavaScript 函数存储在数组中吗?

我可以将函数的输出参数存储到 unique_ptr 中吗?

您可以将函数存储在 PHP 数组中吗?

我应该将 JWT 令牌存储在 IndexedDB 中吗?

我应该将 HTML 表单存储在数据库中吗

我应该将 Firebase 凭据存储在 iCloud 中吗?