Redux: mapStateToProps () no se llama

Creado en 13 may. 2016  ·  4Comentarios  ·  Fuente: reduxjs/redux

He leído toneladas de documentación, en todos los sitios populares, pero en ninguna parte he encontrado una respuesta. Estoy preguntando aquí para ver si me he perdido alguna documentación o un entendimiento vital.

El problema que veo es que la función mapStateToProps () de mi componente solo se llama una vez (en la inicialización). Nunca se vuelve a llamar, incluso cuando se llama al reductor. Empiezo a pensar que es porque no entiendo cómo las acciones, los reductores y los accesorios están relacionados.

Permítanme decir primero lo que yo sé:

  • Entiendo que las acciones describen actualizaciones a una parte (una propiedad) de esa tienda.
  • Entiendo que cada reductor es responsable de actualizar su porción / porción de la tienda. (Y definitivamente entiendo que el reductor debe devolver un nuevo objeto para su segmento).
  • Entiendo que mapStateToProps "extrae" ciertos datos de la tienda y los pasa como accesorios al componente nombrado.

Pero ... no he encontrado una descripción de cómo las acciones, los reductores y mapStateToProps se relacionan entre sí. Mis preguntas:

  • Pienso en la tienda como un objeto, con propiedades que contienen datos sobre el estado de la aplicación. Esas propiedades de nivel superior pueden ser objetos que proporcionan un subárbol para contener información sobre una parte de la aplicación. ¿Es esto cierto?
  • Cuando se envía una acción a la tienda, ¿qué propiedad (o subárbol de la tienda) se actualiza? Parece que hay una creación automática de / asociación a una propiedad dentro de la tienda, pero ¿cómo se determina eso?
  • ¿Cómo "sabe" mapStateToProps escuchar las actualizaciones de la propiedad / parte adecuada de la tienda? ¿Qué los une?
  • ¿Cómo podría depurar esto? ¿Qué debería hacer para descubrir por qué el envío de la acción no parece disparar mapStateToProps?
  • ¿O lo estoy pensando todo mal ...?
question

Comentario más útil

¿Ha revisado http://redux.js.org/docs/Troubleshooting.html y verificado que su caso no es uno de los problemas que describe?

Pienso en la tienda como un objeto, con propiedades que contienen datos sobre el estado de la aplicación. Esas propiedades de nivel superior pueden ser objetos que proporcionan un subárbol para contener información sobre una parte de la aplicación. ¿Es esto cierto?

Sí. La tienda contiene el objeto de estado de nivel superior internamente. Proporciona acceso a ese objeto desde store.getState() . Ese objeto corresponde a lo que sea que devuelva del reductor de raíz y, a menudo, tiene forma de árbol.

Cuando se envía una acción a la tienda, ¿qué propiedad (o subárbol de la tienda) se actualiza? Parece que hay una creación automática de / asociación a una propiedad dentro de la tienda, pero ¿cómo se determina eso?

No, no hay creación automática de nada. La tienda simplemente comienza a señalar el resultado de llamar a su reductor de raíz.

Si su reductor de raíz es una función que escribió usted mismo, su resultado es lo que obtendrá después de enviar una acción:

function counter(state = 0, action) {
  if (action.type === 'INCREMENT') {
    return state + 1; // will become the next value of store.getState() after store.dispatch()
  }
  return state;
}

Si _genera_ el reductor de raíz con algo como combineReducers({ foo, bar }) , es lo mismo que si escribiera un reductor de raíz a mano que se ve así:

function root(state = {}, action) {
  return {
    foo: foo(state.foo, action),
    bar: bar(state.bar, action)
  }
}

Aquí es de donde a menudo provienen las propiedades en el estado. Sus valores están determinados por lo que devolvieron sus reductores foo y bar mientras manejaban la acción, respectivamente, por lo que nuevamente usted tiene el control total sobre ellos.

¿Cómo "sabe" mapStateToProps escuchar las actualizaciones de la propiedad / parte adecuada de la tienda? ¿Qué los une?

Pasa la función mapStateToProps a connect() . Genera un componente que usa store.subscribe() para escuchar cada cambio en la tienda. Cuando la tienda ha cambiado, el componente generado lee store.getState() y lo pasa a mapStateToProps() para determinar los siguientes accesorios que se transmitirán. Si cambiaron, volverá a renderizar su componente con ellos.

¿Cómo podría depurar esto? ¿Qué debería hacer para descubrir por qué el envío de la acción no parece disparar mapStateToProps?

Agregue un middleware como https://github.com/theaqua/redux-logger. Verifique que el estado se actualice después de la acción de la forma esperada. Si ni siquiera se llama a mapStateToProps , significa que

  • O olvidó dispatch() una acción y acaba de llamar a un creador de acciones,
  • O su reductor devolvió un valor referencialmente idéntico, por lo que connect() no se molestó en llamar a mapStateToProps() .

Ambos casos se describen en http://redux.js.org/docs/Troubleshooting.html.

En el futuro, le pedimos que primero intente hacer preguntas sobre StackOverflow y que use el rastreador de problemas cuando esté seguro de que hay un error en la biblioteca que puede reproducir de manera consistente con un ejemplo que puede compartir.

¡Gracias!

Todos 4 comentarios

¿Ha revisado http://redux.js.org/docs/Troubleshooting.html y verificado que su caso no es uno de los problemas que describe?

Pienso en la tienda como un objeto, con propiedades que contienen datos sobre el estado de la aplicación. Esas propiedades de nivel superior pueden ser objetos que proporcionan un subárbol para contener información sobre una parte de la aplicación. ¿Es esto cierto?

Sí. La tienda contiene el objeto de estado de nivel superior internamente. Proporciona acceso a ese objeto desde store.getState() . Ese objeto corresponde a lo que sea que devuelva del reductor de raíz y, a menudo, tiene forma de árbol.

Cuando se envía una acción a la tienda, ¿qué propiedad (o subárbol de la tienda) se actualiza? Parece que hay una creación automática de / asociación a una propiedad dentro de la tienda, pero ¿cómo se determina eso?

No, no hay creación automática de nada. La tienda simplemente comienza a señalar el resultado de llamar a su reductor de raíz.

Si su reductor de raíz es una función que escribió usted mismo, su resultado es lo que obtendrá después de enviar una acción:

function counter(state = 0, action) {
  if (action.type === 'INCREMENT') {
    return state + 1; // will become the next value of store.getState() after store.dispatch()
  }
  return state;
}

Si _genera_ el reductor de raíz con algo como combineReducers({ foo, bar }) , es lo mismo que si escribiera un reductor de raíz a mano que se ve así:

function root(state = {}, action) {
  return {
    foo: foo(state.foo, action),
    bar: bar(state.bar, action)
  }
}

Aquí es de donde a menudo provienen las propiedades en el estado. Sus valores están determinados por lo que devolvieron sus reductores foo y bar mientras manejaban la acción, respectivamente, por lo que nuevamente usted tiene el control total sobre ellos.

¿Cómo "sabe" mapStateToProps escuchar las actualizaciones de la propiedad / parte adecuada de la tienda? ¿Qué los une?

Pasa la función mapStateToProps a connect() . Genera un componente que usa store.subscribe() para escuchar cada cambio en la tienda. Cuando la tienda ha cambiado, el componente generado lee store.getState() y lo pasa a mapStateToProps() para determinar los siguientes accesorios que se transmitirán. Si cambiaron, volverá a renderizar su componente con ellos.

¿Cómo podría depurar esto? ¿Qué debería hacer para descubrir por qué el envío de la acción no parece disparar mapStateToProps?

Agregue un middleware como https://github.com/theaqua/redux-logger. Verifique que el estado se actualice después de la acción de la forma esperada. Si ni siquiera se llama a mapStateToProps , significa que

  • O olvidó dispatch() una acción y acaba de llamar a un creador de acciones,
  • O su reductor devolvió un valor referencialmente idéntico, por lo que connect() no se molestó en llamar a mapStateToProps() .

Ambos casos se describen en http://redux.js.org/docs/Troubleshooting.html.

En el futuro, le pedimos que primero intente hacer preguntas sobre StackOverflow y que use el rastreador de problemas cuando esté seguro de que hay un error en la biblioteca que puede reproducir de manera consistente con un ejemplo que puede compartir.

¡Gracias!

Respondiendo en orden:

  • Redux generalmente asume que la parte superior de su árbol de estado es un objeto Javascript simple (aunque también es común usar los tipos de datos proporcionados por la biblioteca Immutable.js). Lo que pones dentro de ese objeto y cómo estructuras el contenido depende completamente de ti. El enfoque más común es organizar las cosas por dominio. Por ejemplo, un árbol de estado para una aplicación de blog hipotética podría verse así: { users : {}, posts : {}, comments : {}, ui : {} } . Consulte http://redux.js.org/docs/FAQ.html#reducers -share-state y http://redux.js.org/docs/FAQ.html#organizing -state-nested-data.
  • Técnicamente hablando, cuando crea su tienda, define una función "reductora" _single_. Esa función se llama con el estado actual y la acción entrante, y es responsable de devolver un nuevo objeto de estado, con _todas_ las actualizaciones apropiadas, realizadas de manera inmutable (es decir, sin ediciones directas en el lugar; todo lo que debe actualizarse debe ser hacer copias, modificarlas y devolver las copias en su lugar, y actualizar un campo anidado significa también copiar y actualizar todos sus antepasados). Sin embargo, dado que poner hasta el último bit de la lógica de actualización en una función gigante es una mala idea, esa lógica de actualización casi siempre se divide en funciones de sub-reductor reutilizables más pequeñas. Cómo estructurar esa lógica depende nuevamente de usted, pero nada se actualiza automáticamente; depende de su lógica reductora hacer eso. La utilidad combineReducers que viene con Redux hace que sea fácil tener una función reductora específica que sea responsable de administrar las actualizaciones de un segmento de datos determinado, como: const combinedReducer = combineReducers({users : userReducer, posts : postReducer, comments : commentsReducer, ui : uiReducer});
  • Redux simplemente notifica a todos los suscriptores cada vez que se ejecuta _cualquier_ acción en la tienda. La función React Redux connect() suscribe y luego llama a mapStateToProps del componente para ver si alguno de los datos extraídos ha cambiado desde la última vez. No hay una suscripción estatal anidada específica. Consulte http://redux.js.org/docs/FAQ.html#store -setup-subscriptions.
  • La causa número uno de que un componente no se actualice es la mutación directa de estado en su reductor. Consulte http://redux.js.org/docs/FAQ.html#react -not-rerendering. Más allá de eso, hay una serie de complementos que puede utilizar para depurar y registrar acciones. Tengo un montón de ellos enumerados en https://github.com/markerikson/redux-ecosystem-links/blob/master/devtools.md.

Con suerte, eso ayuda a aclarar las cosas.

Más allá de eso, como dijo Dan: las preguntas de uso generalmente son más adecuadas para Stack Overflow. Además, la comunidad Reactiflux en Discord tiene un montón de canales de chat dedicados a las tecnologías relacionadas con React, incluido Redux, y siempre hay una cantidad de personas dispuestas a responder preguntas. Hay un enlace de invitación en https://www.reactiflux.com .

@ richb-hanover Probablemente haya sido testigo de la comparación superficial que realiza la conexión react-redux. Esto significa que los objetos anidados dentro de otros objetos no darán lugar a una nueva renderización incluso si se modifican porque solo se comprueba la clave raíz para las modificaciones.

http://redux.js.org/docs/faq/ReactRedux.html#why -isnt-my-component-re-rendering-or-my-mapstatetoprops-running

@ richb-hanover En mi caso, @gaearon detectó el problema:

O su reductor devolvió un valor referencialmente idéntico, por lo que connect () no se molestó en llamar a mapStateToProps ().

Estaba usando esta solución y este fue el código resultante

const mapStateToProps = (state, props) => {
    return { id: props.id, currentState: state[props.id] };
}

const mapDispatchToProps = (dispatch) => {
    return {
        increment: (id) => {
            dispatch({ type: 'INCREMENT', id: id })
        }
    }
}

const DumbListItem = ({
    increment,
    id,
    currentState
}) => (
        <div>
            <li onClick={() => increment()}>{id}</li>
            <span>{currentState}</span>
        </div>
    );

export const ConnectedListItem = connect(
    mapStateToProps,
    mapDispatchToProps
)(DumbListItem);

Como no pasé el parámetro id a la llamada de incremento (ya sea como increment(id) en la plantilla, o mediante el segundo parámetro en mapDispatchToProps ), la tienda no lo hizo actualice correctamente, así que verifique si tal vez ese sea su problema.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

ilearnio picture ilearnio  ·  3Comentarios

benoneal picture benoneal  ·  3Comentarios

ms88privat picture ms88privat  ·  3Comentarios

caojinli picture caojinli  ·  3Comentarios

ramakay picture ramakay  ·  3Comentarios