actions - ¿Debo almacenar referencias de funciones en la tienda Redux?
redux thunk (4)
Estoy intentando construir un atajo de teclado en mi aplicación React / Redux de forma idiomática React / Redux . La forma en que planeo hacer esto es tener el siguiente creador de acción y acción asociada:
registerShortcut(keyCode, actionCreatorFuncReference)
El reductor luego actualizaría un objeto registradoShortcuts en el almacén redux con una asignación de keyCodes a actionCreatorFuncReferences. Luego, mi componente raíz escucharía la clave y vería si hay un código de clave asociado registrado y, si es así, distribuirá la acción asignada a través de la referencia de función del creador de acciones.
Sin embargo, esta sería la primera vez que almaceno referencias de función en mi tienda Redux . Hasta la fecha solo he tenido objetos con claves con valores vainilla (strings, ints, etc.).
Los documentos de Redux dicen: "Debes hacer todo lo posible para que el estado sea serializable. No coloques nada dentro de él que no puedas convertir fácilmente en JSON". ¿Esto sugiere que es una mala idea almacenar tales referencias de funciones en mi tienda Redux? Si es así, ¿cuál es una mejor manera de lograr lo que estoy tratando de hacer en React / Redux?
Un enfoque alternativo es simplemente almacenar el mapeo de keyCodes y las referencias de función en el componente de raíz de reacción, pero eso no se sintió muy similar a Redux ya que ahora el estado de la aplicación no está en la tienda de Redux.
He tenido muy buenos resultados al agregar funciones a mi tienda redux. Si sus funciones actúan como métodos que aplican lógica a los datos, pueden ser increíblemente potentes en toda la aplicación (además JSON.stringify eliminará las funciones de todos modos para que pueda serializar su tienda y guardarla en el almacenamiento local o lo que sea). Tengo un reductor de usuario actual que usa una clase como su estado inicial e incluye un método hasPermissions que se puede llamar en cualquier lugar donde esté conectado el usuario actual. Esto es especialmente bueno porque siempre que asocie currentUser a mis accesorios, no necesito recordar importar algún tipo de utilidad que analizará el usuario para ver si tiene permisos para ver algo. El método ya está en el usuario.
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
No, no debe almacenar referencias de funciones en la tienda redux. No son serializables, y como mencionas, el estado debe ser serializable en todo momento. El enfoque más sencillo que puedo pensar es simplemente mantener el mapa de teclas rápidas en sus ActionCreatorFuncNames.
Soy nuevo en redux, pero de la forma en que lo veo, puedes pasar el código de tecla y un tipo de acción. Entonces, un reductor podría estar escuchando ese tipo de acción y hacer los cambios correspondientes.
Aquí hay un ejemplo usando la biblioteca 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;
};
De esta forma, los atajos de teclado enviarán una acción. El reductor hará los cambios necesarios para el estado. Y finalmente, la aplicación puede volver a renderizarse.
TL; DR: usted no. El estado de la tienda debe ser serializable en todo momento (como respondió Nathan). La forma Redux es a través de enhancers , o la forma Redux-Observable a través de dependencies .
Basado en el example documentos de Redux, lo que quiere es pasar la referencia en su acción (1), ignorarlo como reductor (2) y usarlo en su potenciador (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)
)
Nota: en la medida en que no haya efectos secundarios en sus potenciadores, está listo para comenzar. Tenga en cuenta que podría haber obtenido los refs directamente en los reductores, pero tal enfoque mantiene la referencia en el nivel del reductor y pierde el punto de combineReducers()
. Con un potenciador, los mantiene a todos en un solo lugar ( myRefStore
). Una observación final es que una tienda redux no es una tienda de datos, sino una tienda de estado, por lo que necesitamos manejar funciones y otras cosas no relacionadas con el estado en potenciadores. Puede aprovechar la red troncal del potenciador a Redux-Observable e inyectar myRefStore
través de dependencies .