vida react que inmutable immutable componentes ciclo javascript reactjs redux react-redux

javascript - react - Advertencia: setState(...): no se puede actualizar durante una transición de estado existente con redux/inmutable



immutable js redux (5)

Estoy recibiendo el error

Advertencia: setState (...): no se puede actualizar durante una transición de estado existente (como dentro del render o el constructor de otro componente). Los métodos de renderizado deben ser una función pura de puntales y estado; los efectos secundarios del constructor son un anti-patrón, pero se pueden mover a componentWillMount .

Encontré la causa para ser

const mapStateToProps = (state) => { return { notifications: state.get("notifications").get("notifications").toJS() } }

Si no devuelvo las notificaciones allí, funciona. Pero ¿por qué es eso?

import {connect} from "react-redux" import {removeNotification, deactivateNotification} from "./actions" import Notifications from "./Notifications.jsx" const mapStateToProps = (state) => { return { notifications: state.get("notifications").get("notifications").toJS() } } const mapDispatchToProps = (dispatch) => { return { closeNotification: (notification) => { dispatch(deactivateNotification(notification.id)) setTimeout(() => dispatch(removeNotification(notification.id)), 2000) } } } const NotificationsBotBot = connect(mapStateToProps, mapDispatchToProps)(Notifications) export default NotificationsBotBot import React from "react" class Notifications extends React.Component { render() { return ( <div></div> ) } } export default Notifications

ACTUALIZAR

Sobre la depuración adicional encontré que, lo anterior puede no ser la causa raíz después de todo, puedo hacer que las notificaciones se queden pero necesito eliminar el dispatch(push("/domains")) mi redirección.

Así es como inicio de sesión:

export function doLogin (username, password) { return function (dispatch) { dispatch(loginRequest()) console.log("Simulated login with", username, password) setTimeout(() => { dispatch(loginSuccess(`PLACEHOLDER_TOKEN${Date.now()}`)) dispatch(addNotification({ children: "Successfully logged in", type: "accept", timeout: 2000, action: "Ok" })) dispatch(push("/domains")) }, 1000) } }

Me parece que el envío causa la advertencia, pero ¿por qué? Mi página de dominios no tiene mucho actualmente:

import {connect} from "react-redux" import DomainsIndex from "./DomainsIndex.jsx" export default connect()(DomainsIndex)

DomainsIndex

export default class DomainsIndex extends React.Component { render() { return ( <div> <h1>Domains</h1> </div> ) } }

ACTUALIZACIÓN 2

Mi App.jsx . <Notifications /> es lo que muestra las notificaciones

<Provider store={store}> <ConnectedRouter history={history}> <Layout> <Panel> <Switch> <Route path="/auth" /> <Route component={TopBar} /> </Switch> <Switch> <Route exact path="/" component={Index} /> <Route path="/auth/login" component={LoginBotBot} /> <AuthenticatedRoute exact path="/domains" component={DomainsPage} /> <AuthenticatedRoute exact path="/domain/:id" component={DomainPage} /> <Route component={Http404} /> </Switch> <Notifications /> </Panel> </Layout> </ConnectedRouter> </Provider>


La razón por la que esto sucede es cuando llamas al doLogin , si lo llamas desde un constructor . Si este es el caso, intente moverlo a componentWillMount aunque debe llamar a este método desde un botón o ingresar hit en el formulario de inicio de sesión.

Esto ha sido documentado en el constructor no debería mutar Si esta no es la raíz del problema, importa comentar cada línea en doLogin para saber exactamente qué línea da el problema de estado, mi suposición sería el push o el addNotification


Su dispatch(push(''/domains'')) viene junto con otros despachos que configuran el estado de un componente conectado (presumiblemente uno que se preocupa por las notificaciones) que se vuelve a montar / desmontar después de que la push vigencia.

Como solución temporal y como prueba de concepto, intente aplazar la llamada de dispatch(push(''/domains'')) con un setTimeout segundo cero anidado. Esto debería ejecutar el push después de que termine cualquiera de las otras acciones (es decir, con suerte una renderización):

setTimeout(() => dispatch(push(''/domains'')), 0)

Si eso funciona, entonces es posible que desee reconsiderar su estructura de componentes. Supongo que Notifications es un componente que desea montar una vez y mantenerlo allí durante toda la vida de la aplicación. Intente evitar remontarlo colocándolo en un lugar más alto en el árbol de componentes y convirtiéndolo en un PureComponent (aquí están los documentos ). Además, si la complejidad de su aplicación aumenta, debe considerar el uso de una biblioteca para manejar la funcionalidad asíncrona, como redux-saga .

Aunque esta advertencia generalmente aparece debido a una llamada de acción fuera de lugar (por ejemplo, llamar a una acción en render: onClick={action()} lugar de pasar como lambda: onClick={() => action()} , si sus componentes parece que has mencionado (simplemente renderizando un div ), entonces esa no es la causa del problema.


Tuve este problema en el pasado y logré resolverlo usando acciones reducidas por lotes .

Es muy útil para casos de uso como el tuyo cuando envías múltiples acciones a la vez y no estás seguro de cuándo se activarán las actualizaciones, con esto, solo habrá un único envío para múltiples acciones. En su caso, parece que la addNotification posterior está bien, pero la tercera es demasiado, tal vez porque interactúa con la API de historial.

Intentaría hacer algo como (suponiendo que tu setTimeout será reemplazado por una llamada de API por supuesto):

import { batchActions } from ''redux-batched-actions'' export function doLogin (username, password) { return function (dispatch) { dispatch(loginRequest()) console.log("Simulated login with", username, password) setTimeout(() => { dispatch(batchActions([ loginSuccess(`PLACEHOLDER_TOKEN${Date.now()}`), addNotification({ children: "Successfully logged in", type: "accept", timeout: 2000, action: "Ok" }), push("/domains") ])) }, 1000) } }

Tenga en cuenta que tendrá que descargar el paquete y enableBatching el enableBatching su reductor en la creación de su tienda.


En un componente de reacción cuando llamas a setState({...}) , hace que el componente vuelva a renderizar y llame a todos los métodos del ciclo de vida que están involucrados en la re-interpretación del componente y el método de render es uno de ellos

Cuando está en render si llama a setState({...}) causará una nueva renderización y el renderizado se setState({...}) una y otra vez, por lo tanto, esto disparará un bucle infinito dentro del javascript que eventualmente llevará a la falla de la aplicación

Por lo tanto, no solo en el render sino también en cualquier método del ciclo de vida que sea parte del proceso de setState({...}) , no se debe llamar al método setState({...}) .

En su caso, el código podría estar desencadenando una actualización en el estado de reducción mientras el código todavía está en proceso de renderización y, por lo tanto, esto causa que se vuelva a renderizar y la reacción muestre un error.


No hay suficiente información para dar una respuesta determinada. Pero lo que es seguro es que esta advertencia se plantea cuando setState método de render dentro de setState .

La mayoría de las veces sucede cuando llamas las funciones de tu controlador en lugar de pasarlas como accesorios al Component hijo. Como sucedió aquí o aquí .

Por lo tanto, mi consejo es verificar qué Components se están procesando en su ruta /domains y cómo está onChange controladores de onChange , onClick , etc. a ellos y a sus hijos.