首页 文章

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

提问于
浏览
33

我正在尝试在 idiomatic React/Redux way 中将 keyboard shortcut support 构建到我的React / Redux应用程序中 . 我计划这样做的方法是拥有以下动作创建者和相关动作:

registerShortcut(keyCode, actionCreatorFuncReference)

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

但是,这将是我第一次 storing function references in my Redux store . 到目前为止,我只有带有香草值的键的对象(字符串,整数等) .

Redux文档说"You should do your best to keep the state serializable. Don’t put anything inside it that you can’t easily turn into JSON." . Does this suggest it's a bad idea to store such function references in my Redux store? 如果是这样,有什么更好的方法来完成我在React / Redux中尝试做的事情?

另一种方法是在根反应组件本身中存储keyCodes和函数引用的映射,但是由于现在应用程序状态不在Redux存储中,因此感觉不像Redux .

4 回答

  • 27

    不,您不应该在redux商店中存储函数引用 . 它们不是可序列化的,正如您所提到的,状态应该是可序列化的 . 我能想到的最友好的redux方法就是将热键 Map 保存到actionCreatorFuncNames中 .

  • -1

    TL;DR: 你没有 . 商店状态必须始终可序列化(如Nathan回答) . 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:data:, ref: ref}; //(1)
    
    //... in your reducer:
    case 'ACTION_WITH_REF':
    return {...state, data: action.data}
    
    //... 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 redux-observable
         case 'ACTION_WITH_REF':
         myRefStore.aRef = action.ref // (3)
         break;
     }
        const returnValue = next(action)   
        return returnValue
      }
    } 
    const store = createStore(
      reducers,
      initialState,
      applyMiddleware(refHandler)
    )
    

    Note: 至于你的增强剂没有副作用,你很高兴 . 请注意,您可以直接在reducer中获取refs,但是这种方法会将引用保持在reducer级别并且忽略 combineReducers() 的点 . 使用增强器,您可以将它们保存在一个位置( myRefStore ) . 最后一个观察结果是,redux存储不是任何数据存储而是状态存储,因此我们需要在增强器中处理函数和其他非状态相关的东西 . 您可以利用增强器主干Redux-Observable并通过dependencies注入 myRefStore .

  • 5

    我是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;
    };
    

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

  • 1

    我在redux商店中添加了一些非常好的结果 . 如果您的函数充当将逻辑应用于数据的方法,那么它在整个应用程序中都会非常强大(加上JSON.stringify无论如何都会删除函数,因此您仍然可以序列化您的商店并将其保存到本地存储或其他任何位置 . )我有一个currentUser reducer,它使用一个类作为它的初始状态,并包含一个hasPermissions方法,可以在currentUser连接的任何地方调用 . 这是特别好的,因为每当我将currentUser映射到我的道具时,我都不需要记住导入某种类型的util,它会解析用户以查看它是否有权查看某些内容 . 该方法已经在用户身上 .

    class CurrentUser {
        constructor() {
          this.permissions = []
          this.roles = []
          this.admin = false
        }
    
        hasPermission = (permission) => {
          if (this.admin) { return true }
          if (Array.isArray(permission)) {
            return permission.reduce((prev, curr) => {
              if (!prev) { return false }
              if (this.permissions.indexOf(curr) > -1) { return true }
              return false
            }, true)
          }
          return this.permissions.indexOf(permission) > -1
        }
      }
    
      const initialState = new CurrentUser()
    
      const curretUserReducer = (state = initialState, action) => {
        const { type, payload } = action
        switch (type) {
          case ACTIONS.SET_CURRENT_USER: {
            const { user } = payload
            return Object.assign(new CurrentUser(), user)
          }
          case ACTIONS.CLEAR_CURRENT_USER: {
            return new CurrentUser()
          }
          case ACTIONS.UPDATE_CURRENT_USER: {
            const newCurrentUser = merge(state, payload.user)
            storage.set('currentUser', JSON.stringify(newCurrentUser))
            return newCurrentUser
          }
          default:
            return state
        }
      }
    
      export default curretUserReducer
    

相关问题