2016-03-15 39 views
8

Provare React + Redux, e probabilmente sto facendo qualcosa ovviamente stupido, perché un componente che lancia un'azione per recuperare i dati sulla rete non viene aggiornato (ri-renderizzato) quando i dati è recuperato.React + Redux: il componente non si aggiorna

Ecco i bit rilevanti del mio codice:

I index.js di alto livello che servono come punto di ingresso per l'applicazione:

import React from 'react'; 
import ReactDOM from 'react-dom'; 
import { Provider } from 'react-redux'; 
import { createStore, applyMiddleware } from 'redux'; 
import { Router, browserHistory } from 'react-router'; 
import reduxPromise from 'redux-promise'; 
import createLogger from 'redux-logger'; 

const logger = createLogger(); 

import routes from './routes'; 
import reducers from './reducers'; 

const createStoreWithMiddleware = applyMiddleware(reduxPromise, logger)(createStore); 

ReactDOM.render(
    <Provider store={createStoreWithMiddleware(reducers)}> 
    <Router history={browserHistory} routes={routes} /> 
    </Provider> 
    , document.querySelector('.container')); 

contenitore di livello superiore App:

import React, {Component} from 'react'; 
import { bindActionCreators } from 'redux'; 
import { connect } from 'react-redux'; 
import * as Actions from '../actions'; 
import Header from '../components/header'; 
import Showcase from '../components/showcase'; 

function mapStateToProps(state) { 
    return { 
    resources: state.resources 
    } 
} 

function mapDispatchToProps(dispatch) { 
    return { 
    fetchResources:() => { 
     dispatch(Actions.fetchResources()); 
    } 
    } 
} 


class App extends Component { 

    render() { 
    console.log('props in App', this.props); 
    return (
     <div> 
     <Header/> 
     <Showcase 
      fetchResources={this.props.fetchResources} 
      resources={this.props.resources} 
     /> 
     </div> 
    ); 
    } 
} 

export default connect(
    mapStateToProps, 
    mapDispatchToProps 
)(App) 

Componente che attiva un'azione per inviare una richiesta di dati quando sta per essere montata e deve mostrare i dati recuperati:

import React, {Component} from 'react'; 
import {connect} from 'react-redux'; 

class Showcase extends Component { 
    constructor(props) { 
    super(props); 
    } 

    componentWillMount() { 
    this.props.fetchResources(); 
    } 

    render() { 
    console.log('resources', this.props); 
    return (
     <div> 
     This is showcase 
     </div> 
    ); 
    } 
} 

export default connect(state => ({resources: state.resources}))(Showcase) 

Azione Creatore:

import * as types from '../constants/ActionTypes'; 
import axios from 'axios'; 

export function fetchResources() { 
    return { 
    type: types.FETCH_FIRST, 
    payload: axios.get('/sampledata/1.json') 
    } 
} 

riduttore per l'azione prendere:

import * as types from '../constants/ActionTypes'; 

export default function resourcesReducer (state={}, action) { 
    switch (action.type) { 
    case types.FETCH_FIRST: 
     console.log('about to return', Object.assign (state, {resources: action.payload.data })) 
     return Object.assign (state, {resources: action.payload.data }); 
    default: 
     return state 
    } 
}; 

e, infine, il riduttore di root:

import { combineReducers } from 'redux'; 
import navigationReducer from './navigation-reducer'; 
import resourcesReducer from './resources-reducer'; 

const rootReducer = combineReducers({ 
    navigationReducer, 
    resourcesReducer 
}); 

export default rootReducer; 

Quindi, ecco quello che sto osservando. L'azione per richiedere i dati viene attivata correttamente, viene inviata una richiesta, il riduttore lo riceve quando la promessa viene risolta e aggiorna lo stato con i dati recuperati. A questo punto, mi aspetto che il componente di livello superiore App e il componente Showcase rilevi che lo store è stato aggiornato e che è stato eseguito il re-rendering, ma non lo vedo nella console.

Inoltre, sono confuso da redux-logger s' output della console:

enter image description here

In particolare, sto sorpresi di vedere che il state contiene riduttori dal rootReducer - non so se è giusto (un esempio su Redux logger Github page mostra uno stato senza riduttori). Sembra anche sorprendente che il prev state come riportato da redux-logger contenga lo stesso oggetto resourcesReducer come next state, anche se intuitivamente mi aspetterei che prev state sia più o meno vuoto.

Potrebbe per favore indicare cosa sto facendo male e come ottenere i componenti React rispondono alle modifiche state?

========================================= ====

aggiornamento:

1) Modificato la funzione mapStateToProps nel componente App modo che associa correttamente al riduttore stati:

function mapStateToProps(state) { 
    return { 
    resources: state.resourcesReducer 
    } 
} 

2) Ancora passa il resources giù il componente `Showcase:

render() { 
    console.log('props in App', this.props); 
    return (
     <div> 
     <Header navigateActions={this.props.navigateActions}/> 
     React simple starter 
     <Showcase 
      fetchResources={this.props.fetchResources} 
      resources={this.props.resources} 
     /> 
     </div> 
    ); 

3) Cercare di visualizzare resources sullo schermo stringifying per vedere che cosa in realtà all'interno di questo oggetto:

render() { 
    console.log('resources', this.props); 
    return (
     <div> 
     This is showcase {JSON.stringify(this.props.resources)} 
     </div> 
    ); 
    } 

Vai a questa sullo schermo: This is showcase {}. Il componente non sembra ri-render.

Ecco lo screenshot della console che mostra che i puntelli dell'app sono stati aggiornati con i valori dello next state. Ancora, che non ha causato la componente di ri-renderizzare:

enter image description here

nuovamente aggiornato: E il mio javascript-fu era povera, troppo. Non mi rendevo conto che restituendo Object.assign (state, {resources: action.payload.data }); stavo effettivamente mutando lo stato, e che una semplice inversione di argomenti mi avrebbe permesso di ottenere ciò che volevo. Grazie a this discussion su SO per l'illuminazione.

+0

hai provato ad aggiungere la chiave all'oggetto riduttore radice? 'const rootReducer = combineReducers ({ navigation: navigationReducer, resources: resourcesReducer });' – anoop

risposta

6

sto sorpresi di vedere che lo Stato contiene riduttori dal rootReducer

Questo è come funziona. Dai uno sguardo più da vicino a combineReducers().

const rootReducer = combineReducers({ 
    navigationReducer, 
    resourcesReducer 
}); 

Riconoscere che non si tratta di un elenco di parametri; è un parametro oggetto singolo. Forse è più chiaro nella sintassi ES5:

var rootReducer = combineReducers({ 
    navigationReducer: navigationReducer, 
    resourcesReducer: resourcesReducer 
}); 

I resourcesReducer punti chiave allo stato restituito dalla funzione resourcesReducer(). Cioè, la variabile state all'interno dello resourcesReducer() è solo una parte dell'intero stato.

Le funzioni passate a connect() considerano l'intero stato come argomento. Quello che dovrebbe essere effettivamente il tuo è questo:

export default connect(state => ({ 
    resources: state.resourcesReducer.resources 
}))(Showcase); 
+0

Grazie. Il tuo commento ha chiarito quali sono le relazioni tra lo stato e i riduttori (e combina i Redattori), ma anche dopo aver aggiornato il mio codice (vedi la sezione AGGIORNATA della mia domanda originale), un aggiornamento dello stato non porta al rerendering di un componente . Ora sottoscrivo solo il mio componente 'App' al negozio e ho cambiato la funzione' mapStateToProps' per restituire '{resources: state.resourcesReducer}'.Vedo che lo stato si aggiorna e che gli oggetti di scena di "App" riflettono questo cambiamento, ma ciò non causa il ri-rendering dell'app (o dello Showcase). Cosa mi manca? – azangru

+1

Ok, ora capisco cosa stava succedendo. Anche il mio 'Object.assign' era sbagliato. Stavo mutando lo stato: 'return Object.assign (state, {resources: action.payload.data})' mentre quello che dovevo veramente fare era restituire un nuovo oggetto (ad esempio 'return Object.assign ({resources: action .payload.data}, stato) '). Silly me. Sembra essere un errore comune però; c'è un post molto simile su SO che ho perso: http://stackoverflow.com/questions/33140008/react-redux-store-updates-but-react-does-not – azangru

Problemi correlati