2014-09-19 22 views
17

Mam klasę reakcji, która przechodzi do interfejsu API, aby uzyskać zawartość. Mam potwierdzone dane wraca, ale to nie jest re-rendering:Komponent reagujący nie renderuje się ponownie przy zmianie stanu

var DealsList = React.createClass({ 
    getInitialState: function() { 
    return { deals: [] }; 
    }, 
    componentDidMount: function() { 
    this.loadDealsFromServer(); 
    }, 
    loadDealsFromServer: function() { 
    var newDeals = []; 

    chrome.runtime.sendMessage({ action: "findDeals", personId: this.props.person.id }, function(deals) { 
     newDeals = deals; 
    }); 

    this.setState({ deals: newDeals }); 
    }, 
    render: function() { 
    var dealNodes = this.state.deals.map(function(deal, index) { 
     return (
     <Deal deal={deal} key={index} /> 
    ); 
    }); 
    return (
     <div className="deals"> 
     <table> 
      <thead> 
      <tr> 
       <td>Name</td> 
       <td>Amount</td> 
       <td>Stage</td> 
       <td>Probability</td> 
       <td>Status</td> 
       <td>Exp. Close</td> 
      </tr> 
      </thead> 
      <tbody> 
      {dealNodes} 
      </tbody> 
     </table> 
     </div> 
    ); 
    } 
}); 

Jednakże jeśli dodać debugger jak poniżej, newDeals są wypełniane, a następnie raz ja nadal widzę dane:

loadDealsFromServer: function() { 
    var newDeals = []; 

    chrome.runtime.sendMessage({ action: "findDeals", personId: this.props.person.id }, function(deals) { 
     newDeals = deals; 
    }); 
    debugger 
    this.setState({ deals: newDeals }); 
    }, 

to właśnie dzwoni lista promocje:

var Gmail = React.createClass({ 
    render: function() { 
    return (
     <div className="main"> 
     <div className="panel"> 
      <DealsList person={this.props.person} /> 
     </div> 
     </div> 
    ); 
    } 
}); 

Odpowiedz

8

to dlatego, że odpowiedź od chrome.runtime.sendMessage jest asynchroniczna; oto kolejność operacji:

var newDeals = []; 

// (1) first chrome.runtime.sendMessage is called, and *registers a callback* 
// so that when the data comes back *in the future* 
// the function will be called 
chrome.runtime.sendMessage({...}, function(deals) { 
    // (3) sometime in the future, this function runs, 
    // but it's too late 
    newDeals = deals; 
}); 

// (2) this is called immediately, `newDeals` is an empty array 
this.setState({ deals: newDeals }); 

Po wstrzymaniu skrypt z debuggera, dajesz czas przedłużacza, aby zadzwonić do wywołania zwrotnego; do czasu kontynuacji dane pojawią się i wydaje się, że działają.

Aby ustalić, chcesz wykonać połączenie setState po dane wraca z rozszerzenia Chrome:

var newDeals = []; 

// (1) first chrome.runtime.sendMessage is called, and *registers a callback* 
// so that when the data comes back *in the future* 
// the function will be called 
chrome.runtime.sendMessage({...}, function(deals) { 
    // (2) sometime in the future, this function runs 
    newDeals = deals; 

    // (3) now you can call `setState` with the data 
    this.setState({ deals: newDeals }); 
}.bind(this)); 
+0

Duh! Powinienem był wiedzieć, że tak było, kiedy linia debuggera je naprawiła. Wiązanie (to) było tym, co przeoczyłem, gdy po raz pierwszy spróbowałem. Dzięki, świetny szczegółowy komentarz! – brandonhilkert

32

Chciałbym dodać do tego ogromnie proste, ale i tak łatwo się błędem pisanie:

this.state.something = 'changed'; 

... a potem nie rozumiejąc, dlaczego to nie jest trójwymiarowa i Googling i pochodzących z tej strony, tylko uświadomić sobie, że nie powinno być napisane:

this.setState({something: 'changed'}); 

Reakcja uruchamia ponowne renderowanie, jeśli używasz setState do aktualizacji stanu.

+0

To jest dokładny problem, który miałem. To dziwne, że nie rzucają ostrzeżenia, ponieważ rzucają je, próbując zaktualizować rekwizyty. – AndrewJM

+0

Dzięki dobry panie – heinst

+0

@AndrewJM Nie mogą rzucić ostrzeżenia. Mogliby napisać 'this.state = 'something'', ponieważ uderzałbyś w settera dla' state', ale w powyższym przykładzie kod trafia w gettera, który zwraca obiekt, a następnie kończy ustawienie pole na obiekcie, które jest tylko kopią stanu. –

6

Kolejny, bardzo łatwy błąd, który był źródłem problemu dla mnie: Napisałem własną metodę shouldComponentUpdate, która nie sprawdzała nowej zmiany stanu, którą dodałem.

Powiązane problemy