2016-03-15 32 views
8

Próbujesz React + Redux, i prawdopodobnie robię coś oczywiście głupiego, ponieważ komponent, który uruchamia akcję pobierania danych przez sieć, nie jest aktualizowany (ponownie renderowany), gdy dane jest pobierane.React + Redux: Komponent nie aktualizuje

Oto stosowne fragmenty mojego kodu:

W index.js najwyższego poziomu służące jako punkt wyjścia dla aplikacji:

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')); 

kontenerem najwyższego poziomu aplikacji:

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) 

Składnik, który uruchamia akcję wysłania żądania danych, gdy ma zamiar się zamontować i ma pokazywać pobrane dane:

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) 

Action Twórca:

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

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

redukcyjna dla działania zwrcania:

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 
    } 
}; 

i wreszcie reduktor root:

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

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

export default rootReducer; 

Tak, o to co ja obserwując. Działanie w celu żądania danych zostało pomyślnie uruchomione, żądanie zostaje wysłane, reduktor odbiera je po rozwiązaniu obietnicy i aktualizuje stan za pomocą pobranych danych. W tym momencie oczekiwałbym, że komponent najwyższego poziomu App i komponent Showcase wykryją, że sklep został zaktualizowany i ponownie renderowany, ale nie widzę go w konsoli.

Również jestem zdezorientowany wyjścia konsoli redux-logger „s:

enter image description here

Konkretnie, jestem zaskoczony, aby zobaczyć, że state zawiera reduktory z rootReducer - Nie wiem, czy to prawda (przykład na Redux logger Github page pokazuje stan bez reduktorów). Wydaje się również zaskakujące, że prev state, zgłoszone przez redux-logger, zawiera ten sam obiekt resourcesReducer, co next state, chociaż intuicyjnie spodziewam się, że prev state będzie mniej lub bardziej pusty.

Czy możesz wskazać, co robię źle i jak sprawić, by komponenty React reagowały na zmiany state?

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

AKTUALIZACJA:

1) Zmieniono funkcję mapStateToProps w składniku App tak, że prawidłowo odwzorowuje reduktor stany:

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

2) Nadal przechodzących na resources dół składnik `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) Próba wyświetlenia resources na ekranie przez stringifying go zobaczyć, co jest faktycznie wewnątrz tego obiektu:

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

zobacz na ekranie: This is showcase {}. Komponent nie wydaje się ponownie renderować.

Oto zrzut ekranu z konsoli, wskazujący, że rekwizyty aplikacji zostały zaktualizowane z wartościami z next state. Mimo to, że nie powoduje składnik do ponownego renderowania:

enter image description here

AKTUALIZACJA PONOWNIE: A mój javascript-fu była słaba, zbyt. Nie zdawałem sobie sprawy, że powracając Object.assign (state, {resources: action.payload.data }); faktycznie mutowałem państwo i że prosta inwersja argumentów pozwoli mi osiągnąć to, co zamierzałem. Dzięki this discussion na SO dla oświecenia.

+0

czy próbowałeś dodać klucz do obiektu reduktora root? 'const rootReducer = combineReducers ({ nawigacja: navigationReducer, zasoby: resourcesReducer });' – anoop

Odpowiedz

6

Jestem zaskoczony, aby zobaczyć, że państwo zawiera reduktory z rootReducer

Jak to działa. Przyjrzyj się bliżej combineReducers().

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

Rozpoznanie, że nie jest to lista parametrów; jest to parametr pojedynczego obiektu. Być może jest to wyraźniejsze w składni ES5:

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

W resourcesReducer kluczowych punktów do stanu zwróconego przez funkcję resourcesReducer(). Oznacza to, że zmienna state w obrębie resourcesReducer() to tylko jedna część całego stanu.

Funkcje przekazywane do connect() wziąć cały stan jako argument. To, co naprawdę powinno wyglądać, to:

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

Dziękuję. W swoim komentarzu wyjaśniono, jakie są relacje między państwem a reduktorami (oraz syndykami łączącymi), ale nawet po tym, jak zaktualizowałem swój kod (patrz sekcja AKTUALIZACJA w oryginalnym pytaniu), aktualizacja stanu nie prowadzi do ponownego wydania komponentu . Teraz subskrybuję tylko mój komponent 'App' do sklepu i zmieniłem funkcję' mapStateToProps', aby zwrócić '{resources: state.resourcesReducer}'.Widzę, że aktualizacje stanu i że rekwizyty aplikacji odzwierciedlają tę zmianę, ale to nie powoduje ponownego renderowania aplikacji (lub gabloty). czego mi brakuje? – azangru

+1

Ok, teraz rozumiem co się dzieje. Moje 'Object.assign' było również błędne. Mutowałem stan: 'return Object.assign (state, {resources: action.payload.data})' podczas gdy ja naprawdę musiałem zrobić, to zwrócić nowy obiekt (np. 'Return Object.assign ({resources: action .payload.data}, stan) '). Głupi ja. Wydaje się być powszechnym błędem; istnieje bardzo podobny post na SO, którego przegapiłem: http://stackoverflow.com/questions/33140008/react-redux-store-updates-but-react-does-not – azangru

Powiązane problemy