javascript - react - ¿Acceso al estado de Redux en un creador de acción?
redux saga español (5)
Cuando su escenario es simple, puede usar
import store from ''../store'';
export const SOME_ACTION = ''SOME_ACTION'';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
Pero a veces su
action creator
necesita activar acciones múltiples
por ejemplo, solicitud asíncrona, por lo que necesita las acciones
REQUEST_LOAD
REQUEST_LOAD_SUCCESS
REQUEST_LOAD_FAIL
export const [REQUEST_LOAD, REQUEST_LOAD_SUCCESS, REQUEST_LOAD_FAIL] = [`REQUEST_LOAD`
`REQUEST_LOAD_SUCCESS`
`REQUEST_LOAD_FAIL`
]
export function someAction() {
return (dispatch, getState) => {
const {
items
} = getState().otherReducer;
dispatch({
type: REQUEST_LOAD,
loading: true
});
$.ajax(''url'', {
success: (data) => {
dispatch({
type: REQUEST_LOAD_SUCCESS,
loading: false,
data: data
});
},
error: (error) => {
dispatch({
type: REQUEST_LOAD_FAIL,
loading: false,
error: error
});
}
})
}
}
Nota: necesita redux-thunk para devolver la función en el creador de la acción
Digamos que tengo lo siguiente:
export const SOME_ACTION = ''SOME_ACTION'';
export function someAction() {
return {
type: SOME_ACTION,
}
}
Y en ese creador de acciones, quiero acceder al estado de la tienda global (todos los reductores). ¿Es mejor hacer esto?
import store from ''../store'';
export const SOME_ACTION = ''SOME_ACTION'';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
o esto:
export const SOME_ACTION = ''SOME_ACTION'';
export function someAction() {
return (dispatch, getState) => {
const {items} = getState().otherReducer;
dispatch(anotherAction(items));
}
}
Estoy de acuerdo con @Bloomca. Pasar el valor necesario de la tienda a la función de despacho como argumento parece más simple que exportar la tienda. Hice un ejemplo aquí:
import React from "react";
import {connect} from "react-redux";
import * as actions from ''../actions'';
class App extends React.Component {
handleClick(){
const data = this.props.someStateObject.data;
this.props.someDispatchFunction(data);
}
render(){
return (
<div>
<div onClick={ this.handleClick.bind(this)}>Click Me!</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return { someStateObject: state.someStateObject };
};
const mapDispatchToProps = (dispatch) => {
return {
someDispatchFunction:(data) => { dispatch(actions.someDispatchFunction(data))},
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Hay diferentes opiniones sobre si acceder a los creadores de estado en acción es una buena idea.
Los pocos casos de uso en los que creo que es aceptable es para verificar los datos almacenados en caché antes de realizar una solicitud, o para verificar si está autenticado (en otras palabras, hacer un envío condicional).
Creo que pasar
datos
como
state.something.items
en un creador de acciones es definitivamente un antipatrón y se desaconseja porque oscurece el historial de cambios: si hay un error y los
items
son incorrectos, es difícil rastrear
dónde están
esos incorrectos los valores provienen porque ya son parte de la acción, en lugar de ser calculados directamente por un reductor en respuesta a una acción.
Hazlo con cuidado.
(Para obtener más información sobre los pros y los contras de acceder a los creadores del estado en acción, vea la publicación del blog
Idiomatic Redux: Reflexiones sobre Thunks, Sagas, Abstracción y Reutilización
).
Si encuentra que necesita esto, ambos enfoques que sugirió están bien. El primer enfoque no requiere ningún middleware:
import store from ''../store'';
export const SOME_ACTION = ''SOME_ACTION'';
export function someAction() {
return {
type: SOME_ACTION,
items: store.getState().otherReducer.items,
}
}
Sin embargo, puede ver que depende de que la
store
sea un singleton exportado desde algún módulo.
No lo recomendamos
porque hace que sea mucho más difícil
agregar la representación del servidor a su aplicación
porque en la mayoría de los casos
en el servidor querrá tener una tienda separada por solicitud
.
Entonces, aunque técnicamente este enfoque funciona, no recomendamos exportar una tienda desde un módulo.
Por eso recomendamos el segundo enfoque:
export const SOME_ACTION = ''SOME_ACTION'';
export function someAction() {
return (dispatch, getState) => {
const {items} = getState().otherReducer;
dispatch(anotherAction(items));
}
}
Te requeriría usar el middleware Redux Thunk, pero funciona bien tanto en el cliente como en el servidor. Puede leer más sobre Redux Thunk y por qué es necesario en este caso here .
Idealmente, sus acciones no deben ser "gordas" y deben contener la menor información posible, pero debe sentirse libre de hacer lo que mejor le funcione en su propia aplicación.
Las preguntas frecuentes de Redux tienen información sobre la
división de la lógica entre los creadores y reductores de acciones
y los
momentos en que puede ser útil usar
getState
en un creador de acciones
.
Me gustaría señalar que no es tan malo leer de la tienda; podría ser mucho más conveniente decidir qué hacer en función de la tienda, que pasar todo al componente y luego como un parámetro de Una función. Estoy completamente de acuerdo con Dan en que es mucho mejor no utilizar store como singleteone, a menos que esté 100% seguro de que solo lo usará para la representación del lado del cliente (de lo contrario, podrían aparecer errores difíciles de rastrear).
Creé una biblioteca recientemente para tratar la verbosidad de redux, y creo que es una buena idea poner todo en el middleware, para que tenga todo como inyección de dependencia.
Entonces, su ejemplo se verá así:
import { createSyncTile } from ''redux-tiles'';
const someTile = createSyncTile({
type: [''some'', ''tile''],
fn: ({ params, selectors, getState }) => {
return {
data: params.data,
items: selectors.another.tile(getState())
};
},
});
Sin embargo, como puede ver, realmente no modificamos los datos aquí, por lo que hay una buena posibilidad de que podamos usar este selector en otro lugar para combinarlo en otro lugar.
Presentando una forma alternativa de resolver esto. Esto puede ser mejor o peor que la solución de Dan, dependiendo de su aplicación.
Puede obtener el estado de los reductores en las acciones dividiendo la acción en 2 funciones separadas: primero solicite los datos, segundo acto sobre los datos.
Puede hacerlo utilizando
redux-loop
.
Primero ''solicite amablemente los datos''
export const SOME_ACTION = ''SOME_ACTION'';
export function someAction() {
return {
type: SOME_ACTION,
}
}
En el reductor, intercepte la solicitud y proporcione los datos a la acción de la segunda etapa utilizando
redux-loop
.
import { loop, Cmd } from ''redux-loop'';
const initialState = { data: '''' }
export default (state=initialState, action) => {
switch(action.type) {
case SOME_ACTION: {
return loop(state, Cmd.action(anotherAction(state.data))
}
}
}
Con los datos en la mano, haga lo que quiera inicialmente
export const ANOTHER_ACTION = ''ANOTHER_ACTION'';
export function anotherAction(data) {
return {
type: ANOTHER_ACTION,
payload: data,
}
}
Espero que esto ayude a alguien.