javascript - create - ¿Cómo mostrar un indicador de carga en la aplicación React Redux mientras busca los datos?
react js (8)
¡Quiero decir que son más como eventos que como estado!
Yo no diría eso. Creo que los indicadores de carga son un gran caso de IU que se describe fácilmente como una función de estado: en este caso, de una variable booleana. Si bien esta respuesta es correcta, me gustaría proporcionar un código que la acompañe.
En el
ejemplo
async
en el repositorio de Redux
, el reductor
actualiza un campo llamado
isFetching
:
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
El componente usa
connect()
de React Redux para suscribirse al estado de la tienda y
devuelve
isFetching
como parte del valor de retorno de
mapStateToProps()
para que esté disponible en los accesorios del componente conectado:
function mapStateToProps(state) {
const { selectedReddit, postsByReddit } = state
const {
isFetching,
lastUpdated,
items: posts
} = postsByReddit[selectedReddit] || {
isFetching: true,
items: []
}
return {
selectedReddit,
posts,
isFetching,
lastUpdated
}
}
Finalmente, el componente
usa
isFetching
prop en la función
render()
para representar una etiqueta de "Cargando ..." (que podría ser un spinner):
{isEmpty
? (isFetching ? <h2>Loading...</h2> : <h2>Empty.</h2>)
: <div style={{ opacity: isFetching ? 0.5 : 1 }}>
<Posts posts={posts} />
</div>
}
Aún peor, ¿qué debo hacer cuando tengo que usar el diálogo de confirmación nativo o el diálogo de alerta en las aplicaciones redux / react? ¿Dónde se deben poner, acciones o reductores?
Cualquier efecto secundario (y mostrar un diálogo es sin duda un efecto secundario) no pertenece a los reductores. Piense en los reductores como "constructores de estado" pasivos. Realmente no "hacen" cosas.
Si desea mostrar una alerta, hágalo desde un componente antes de enviar una acción o hágalo desde un creador de acciones. Cuando se despacha una acción, es demasiado tarde para realizar efectos secundarios en respuesta a ella.
Para cada regla, hay una excepción. A veces, su lógica de efectos secundarios es tan complicada que realmente desea unirlos a tipos de acción específicos o a reductores específicos. En este caso, consulte proyectos avanzados como Redux Saga y Redux Loop . Solo haga esto cuando se sienta cómodo con Vanilla Redux y tenga un problema real de efectos secundarios dispersos que le gustaría hacer más manejable.
Soy nuevo en React / Redux. Uso un middleware api fetch en la aplicación Redux para procesar las API. Es ( https://github.com/agraboso/redux-api-middleware ). Creo que es la buena manera de procesar acciones de API asíncronas. Pero encuentro algunos casos que no puedo resolver por mí mismo.
Como dice la página de inicio ( https://github.com/agraboso/redux-api-middleware#lifecycle ), un ciclo de vida de la API de recuperación comienza con el envío de una acción CALL_API y termina con el envío de una acción FSA.
Entonces, mi primer caso es mostrar / ocultar un precargador al buscar API. El middleware enviará una acción de FSA al principio y enviará una acción de FSA al final. Ambas acciones son recibidas por reductores que solo deberían estar haciendo un procesamiento de datos normal. No hay operaciones de IU, no más operaciones. Tal vez debería guardar el estado de procesamiento en estado y luego representarlos cuando se actualiza la tienda.
pero como hacer esto? ¿Un componente de reacción fluye por toda la página? ¿Qué sucede con la actualización de la tienda de otras acciones? ¡Quiero decir que son más como eventos que como estado!
Aún peor, ¿qué debo hacer cuando tengo que usar el diálogo de confirmación nativo o el diálogo de alerta en las aplicaciones redux / react? ¿Dónde se deben poner, acciones o reductores?
¡Los mejores deseos! Deseo de responder.
¿Soy el único que piensa que los indicadores de carga no pertenecen a una tienda Redux? Quiero decir, no creo que sea parte del estado de una aplicación per se ..
Ahora, trabajo con Angular2, y lo que hago es que tengo un servicio de "carga" que expone diferentes indicadores de carga a través de RxJS BehaviourSubjects ... Supongo que el mecanismo es el mismo, simplemente no almaceno la información en Redux.
Los usuarios de LoadingService solo se suscriben a los eventos que desean escuchar.
Mis creadores de acciones de Redux llaman al LoadingService cuando las cosas necesitan cambiar. Los componentes UX se suscriben a los observables expuestos ...
Estoy guardando las URL como ::
isFetching: {
/api/posts/1: true,
api/posts/3: false,
api/search?q=322: true,
}
Y luego tengo un selector memorizado (a través de reselect).
const getIsFetching = createSelector(
state => state.isFetching,
items => items => Object.keys(items).filter(item => items[item] === true).length > 0 ? true : false
);
Para hacer que la url sea única en el caso de POST, paso alguna variable como consulta.
Y donde quiero mostrar un indicador, simplemente uso la variable getFetchCount
Gran respuesta Dan Abramov! Solo quiero agregar que estaba haciendo más o menos exactamente eso en una de mis aplicaciones (manteniendo isFetching como un booleano) y terminé teniendo que hacerlo un número entero (que termina leyendo como el número de solicitudes pendientes) para admitir múltiples simultáneas peticiones.
con booleano:
la solicitud 1 comienza -> la rueda giratoria encendida -> la solicitud 2 comienza -> la solicitud 1 termina -> la rueda giratoria apagada -> la solicitud 2 termina
con entero:
la solicitud 1 comienza -> la rueda giratoria encendida -> la solicitud 2 comienza -> la solicitud 1 termina -> la solicitud 2 termina -> la rueda giratoria apagada
case REQUEST_POSTS:
return Object.assign({}, state, {
isFetching: state.isFetching + 1,
didInvalidate: false
})
case RECEIVE_POSTS:
return Object.assign({}, state, {
isFetching: state.isFetching - 1,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAt
Me gustaría agregar algo.
El ejemplo del mundo real usa un campo
isFetching
en la tienda para representar cuándo se está recuperando una
colección
de artículos.
Cualquier colección se generaliza a un reductor de
pagination
que se puede conectar a sus componentes para rastrear el estado y mostrar si una colección se está cargando.
Me ocurrió que quería obtener detalles para una entidad específica que no encaja en el patrón de paginación. Quería tener un estado que representara si los detalles se están obteniendo del servidor, pero tampoco quería tener un reductor solo para eso.
Para resolver esto, agregué otro reductor genérico llamado
fetching
.
Funciona de manera similar al reductor de paginación y su responsabilidad es solo
observar
un conjunto de acciones y generar un nuevo estado con pares
[entity, isFetching]
.
Eso permite
connect
el reductor a cualquier componente y saber si la aplicación está obteniendo datos actualmente no solo para una colección sino para una entidad específica.
No me topé con esta pregunta hasta ahora, pero como no se acepta ninguna respuesta, arrojaré mi sombrero. Escribí una herramienta para este mismo trabajo: react-loader-factory . Está sucediendo un poco más que la solución de Abramov, pero es más modular y conveniente, ya que no quería tener que pensar después de escribirlo.
Hay cuatro piezas grandes:
-
Patrón de fábrica: esto le permite llamar rápidamente a la misma función para configurar qué estados significan "Carga" para su componente y qué acciones enviar.
(Esto supone que el componente es responsable de iniciar las acciones en las que espera).
const loaderWrapper = loaderFactory(actionsList, monitoredStates);
-
Wrapper: El componente que Factory produce es un "componente de orden superior" (como lo que
connect()
devuelve en Redux), de modo que puede atornillarlo a sus cosas existentes.const LoadingChild = loaderWrapper(ChildComponent);
-
Interacción de acción / reductor: el contenedor verifica si un reductor al que está conectado contiene palabras clave que le dicen que no pase al componente que necesita datos.
Se espera que las acciones despachadas por el contenedor produzcan las palabras clave asociadas (la forma en que redux-api-middleware distribuye
ACTION_SUCCESS
yACTION_REQUEST
, por ejemplo). (Por supuesto, puede enviar acciones a otro lugar y simplemente monitorear desde el contenedor si lo desea). - Throbber: el componente que desea que aparezca mientras los datos de los que depende no está listo. Agregué un pequeño div allí para que pueda probarlo sin tener que armarlo.
El módulo en sí es independiente de redux-api-middleware, pero eso es con lo que lo uso, así que aquí hay un código de muestra de README:
Un componente con un cargador que lo envuelve:
import React from ''react'';
import { myAsyncAction } from ''../actions'';
import loaderFactory from ''react-loader-factory'';
import ChildComponent from ''./ChildComponent'';
const actionsList = [myAsyncAction()];
const monitoredStates = [''ASYNC_REQUEST''];
const loaderWrapper = loaderFactory(actionsList, monitoredStates);
const LoadingChild = loaderWrapper(ChildComponent);
const containingComponent = props => {
// Do whatever you need to do with your usual containing component
const childProps = { someProps: ''props'' };
return <LoadingChild { ...childProps } />;
}
Un reductor para que el cargador supervise (aunque puede conectarlo de manera diferente si lo desea):
export function activeRequests(state = [], action) {
const newState = state.slice();
// regex that tests for an API action string ending with _REQUEST
const reqReg = new RegExp(/^[A-Z]+/_REQUEST$/g);
// regex that tests for a API action string ending with _SUCCESS
const sucReg = new RegExp(/^[A-Z]+/_SUCCESS$/g);
// if a _REQUEST comes in, add it to the activeRequests list
if (reqReg.test(action.type)) {
newState.push(action.type);
}
// if a _SUCCESS comes in, delete its corresponding _REQUEST
if (sucReg.test(action.type)) {
const reqType = action.type.split(''_'')[0].concat(''_REQUEST'');
const deleteInd = state.indexOf(reqType);
if (deleteInd !== -1) {
newState.splice(deleteInd, 1);
}
}
return newState;
}
Espero que en un futuro cercano agregue cosas como tiempo de espera y error al módulo, pero el patrón no será muy diferente.
La respuesta corta a su pregunta es:
- Ate el renderizado al código de renderizado: use un contenedor alrededor del componente que necesita renderizar con los datos como el que mostré arriba.
- Agregue un reductor que haga que el estado de las solicitudes alrededor de la aplicación que le interese sea fácilmente digerible, para que no tenga que pensar demasiado en lo que está sucediendo.
- Los eventos y el estado no son realmente diferentes.
- El resto de tus intuiciones me parecen correctas.
Puede agregar oyentes de cambio a sus tiendas, utilizando
connect()
de React Redux o el método
store.subscribe()
bajo nivel.
Debe tener el indicador de carga en su tienda, que el controlador de cambios de la tienda puede verificar y actualizar el estado del componente.
El componente representa el precargador si es necesario, según el estado.
alert
y
confirm
no debería ser un problema.
Están bloqueando y la alerta ni siquiera recibe ninguna entrada del usuario.
Con
confirm
, puede establecer el estado en función de lo que el usuario ha hecho clic si la elección del usuario debe afectar la representación del componente.
De lo contrario, puede almacenar la opción como variable miembro del componente para su uso posterior.
Tenemos tres tipos de notificaciones en nuestra aplicación, todas las cuales están diseñadas como aspectos:
- Indicador de carga (modal o no modal basado en el accesorio)
- Ventana emergente de error (modal)
- Notificación snackbar (no modal, cierre automático)
Los tres están en el nivel superior de nuestra aplicación (Principal) y están conectados a través de Redux como se muestra en el fragmento de código a continuación. Estos accesorios controlan la visualización de sus aspectos correspondientes.
Diseñé un proxy que maneja todas nuestras llamadas a la API, por lo que todos los errores isFetching y (api) están mediados por actionCreators que importo en el proxy. (Por otro lado, también uso webpack para inyectar una simulación del servicio de respaldo para desarrolladores para que podamos trabajar sin dependencias del servidor).
Cualquier otro lugar en la aplicación que necesite proporcionar cualquier tipo de notificación simplemente importa la acción apropiada. Snackbar & Error tienen parámetros para mostrar los mensajes.
@connect(
// map state to props
state => ({
isFetching :state.main.get(''isFetching''), // ProgressIndicator
notification :state.main.get(''notification''), // Snackbar
error :state.main.get(''error'') // ErrorPopup
}),
// mapDispatchToProps
(dispatch) => { return {
actions: bindActionCreators(actionCreators, dispatch)
}}
) exportar clase predeterminada Principal extiende React.Component {