tutorial reducer react example creators reactjs redux react-redux

reactjs - reducer - Mi estado de Redux ha cambiado, ¿por qué React no desencadena una nueva representación?



react-redux npm (1)

Estoy tratando de diseñar un componente de notificación donde aparecerán notificaciones en ciertas ocasiones (como problemas de conexión, modificaciones exitosas, etc.).

Necesito que las notificaciones desaparezcan después de un par de segundos, así que estoy activando un cambio de estado para eliminar la notificación del estado Redux de setTimeout dentro del componentDidMount la notificación.

Puedo ver que el estado cambia, pero React-Redux no vuelve a representar el componente principal, por lo que la notificación sigue apareciendo en el DOM.

Aquí está mi reductor Redux:

const initialState = { notifications: [] } export default function (state = initialState, action) { switch(action.type) { case CLEAR_SINGLE_NOTIFICATION: return Object.assign ({}, state, { notifications: deleteSingleNotification(state.notifications, action.payload) }) case CLEAR_ALL_NOTIFICATIONS: return Object.assign ({}, state, { notifications: [] }) default: return state } } function deleteSingleNotification (notifications, notificationId) { notifications.some (function (notification, index) { return (notifications [index] [''id''] === notificationId) ? !!(notifications.splice(index, 1)) : false; }) return notifications; }

y mis componentes React ( Main y Notification ):

/* MAIN.JS */ class Main extends Component { renderDeletedVideoNotifications() { console.log(''rendering notifications''); const clearNotification = this.props.clearNotification; return this.props.notifications.map((notification)=> { return <Notification key={notification.id} message={notification.message} style={notification.style} clearNotification={clearNotification} notificationId={notification.id} /> }); } render() { console.log(''rerendering''); return ( <div className="_main"> <Navbar location={this.props.location} logStatus={this.props.logStatus} logOut={this.logout.bind(this)}/> <div className="_separator"></div> {this.props.children} <BottomStack> {this.renderDeletedVideoNotifications()} </BottomStack> </div> ); } } function mapStateToProps(state) { return {logStatus: state.logStatus, notifications: state.notifications.notifications}; } function mapDispatchToProps(dispatch) { return bindActionCreators({checkLogStatus, logOut, clearNotification, clearAllNotifications}, dispatch); } export default connect(mapStateToProps, mapDispatchToProps)(Main); /* NOTIFICATION.JS */ export default class Notification extends Component{ constructor(props){ super(props); this.state = {show: true} } componentWillReceiveProps(nextProps){ if(nextProps.message){ this.setState({show: true}); } } clearNotification(notificationId){ this.props.clearNotifications(notificationId); } componentDidMount(){ console.log(''notification mount''); setTimeout(()=>{ console.log(''timed out''); this.props.clearNotification(this.props.notificationId); }, 1000); } closeNotification(){ this.props.clearNotification(this.props.notificationId); this.setState({show: false}); } render(){ const notificationStyles = () =>{ if (this.props.style === "error"){ return {backgroundColor: ''rgba(152, 5, 19, 0.8)''} } return {backgroundColor: ''rgba(8, 130, 101, 0.8)''} }; if(!this.state.show){ return null; } return ( <div className="notification" style={notificationStyles()}> <div className="notificationCloseButton" onClick={this.closeNotification.bind(this)}> <i className="material-icons">close</i> </div> {this.props.message} </div> ) } };


Tienes todo conectado correctamente, pero te falta un concepto clave para Redux:

Con Redux, nunca se muta ninguna parte del state .

De la guía Redux :

Cosas que nunca debes hacer dentro de un reductor:

  • Muta sus argumentos;
  • Realiza efectos secundarios como llamadas a API y transiciones de enrutamiento;
  • Llame a funciones no puras, por ejemplo, Date.now () o Math.random ().

En deleteSingleNotification , estás usando .splice para cortar la notificación anterior de tu matriz. En su lugar, debe devolver una nueva matriz con la notificación no deseada que falta. La forma más sencilla de hacerlo es con la función .filter:

function deleteSingleNotification(notifications, notificationId){ return notifications.filter (notification => { return notification.id !== notificationId } } ¡Aquí hay un JSBin con su sistema de notificación de trabajo!

Entonces, este es el motivo por el que funciona: el trabajo de React-Redux es actualizar sus componentes siempre que se cambie una parte específica de su tienda Redux. Utiliza una prueba === en cada parte del árbol de estado para saber si algo cambió.

Cuando vas y cambias el estado con algo como .splice, comprueba y piensa que nada es diferente.

Aquí hay un ejemplo para demostrar el problema:

var array = [ ''a'', ''b'', ''c'' ] var oldArray = array array.splice (1, 1) // cut out ''b'' oldArray === array // => true! Both arrays were changed by using .splice, // so React-Redux *doesn''t* update anything

En su lugar, React-Redux necesita que hagamos esto:

var array = [ ''a'', ''b'', ''c'' ] var oldArray = array array = array.filter (item, index => index !== 1) // new array without ''b'' oldArray === array // false. That part of your state has changed, so your // componenet is re-rendered

Redux utiliza este enfoque por razones de rendimiento. Se tarda mucho tiempo en recorrer un gran árbol de estados para ver si todo es igual. Cuando mantienes tu árbol inmutable , solo se necesita una prueba === y el proceso se vuelve mucho más fácil.