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

时间:2016-02-10 20:24:43

标签: redux

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

registerShortcut(keyCode, actionCreatorFuncReference)

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

但是,这是我第一次在我的Redux商店中存储函数引用。到目前为止,我只有带有香草值的键(字符串,整数等)的对象。

Redux文档说"你应该尽力保持状态可序列化。不要在里面放任何你不能轻易变成JSON的东西。"。 这是否表明在我的Redux商店中存储此类函数引用是一个坏主意?如果是这样,有什么更好的方法来完成我在React / Redux中尝试做的事情?

另一种方法是将keyCodes和函数引用的映射存储在根反应组件本身中,但是由于现在应用程序状态不在Redux存储中,所以它并不像Redux那样。

4 个答案:

答案 0 :(得分:29)

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

答案 1 :(得分:7)

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)
)

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:-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