2016-07-31 9 views
1

ich total Neuling bin zu ReactJS und es ist ein wenig seltsam Verhalten, wenn ich erstellt Zähler AppReactJS mit ES6 Klasse im Zustand: Änderung nur einmal

https://jsfiddle.net/8f5tqr74/2/

class CounterViewModel { 
    constructor() { 
     this.value = 5; 
    } 

    tick() { 
     //cannot move logic of this method to sendTick complex logic will be here 
     this.value++; 
    } 

} 

var Counter = React.createClass({ 
    getInitialState: function() { 
     return new CounterViewModel(); 
    }, 

    sendTick: function() { 
     console.log("Before:",this.state); 

     var stateObj = this.state; 
     stateObj.tick(); 
     this.setState(stateObj); 
     console.log("After:",this.state); 
    }, 

    render: function() { 
     return <div> 
      {this.state.value} 
      <button onClick={this.sendTick}>Increase</button> 
     </div> 
    } 


}); 


ReactDOM.render(<Counter />, document.getElementById('container')); 

Mein Problem ist Dieser Code funktioniert nur erstes Mal, wenn ich klicken Nachdem ich dies in Developer Konsole

//first click 
Before: CounterViewModel {value: 5} 
After: CounterViewModel {value: 6} 
//second click 
Before: Object {value: 6} 

im zweiten Klick cla habe s Zustand gespült Objekt, jedoch habe ich nicht machen Manipulationen in diesem Objekt

Meine Fragen sind:

1) Warum reagiert bündig Objektklasse nach setState?

2) Warum reagiert nicht flush Klassenobjekt nach getInitialState?

3) Wie kann ich komplexe Logik in Zustandsobjekt in React setzen? Ich weiß, ich Objekt mit einem erhöhten Wert neu erstellen kann und alles wird gut funktionieren (siehe Code unten)

class CounterViewModel { 
    constructor(value) { 
     this.value = value; 
    } 
} 

var Counter = React.createClass({ 
    getInitialState: function() { 
     return new CounterViewModel(5); 
    }, 

    sendTick: function() { 
     this.setState(new CounterViewModel(this.state.value+1)) 
    }, 

    render: function() { 
     return <div> 
      {this.state.value} 
      <button onClick={this.sendTick}>Increase</button> 
     </div> 
    } 


}); 


ReactDOM.render(<Counter />, document.getElementById('container')); 

Antwort

2

Reaktion erwartet state ein einfaches JS-Objekt zu sein, das ist der Grund, warum es ein Objekt wird, nachdem Sie setState() nennen.

Auch setState nicht sofort this.state mutieren, was bedeutet, dass, wenn es in Ihrem Code druckt

After: CounterViewModel {value: 6} 

die state nicht wirklich ändern, wenn Sie diese console.log bewegen() an die Callback-Funktion von setState wie in diesen jsfiddle, Sie, dass nach den Zustandsänderungen auffallen wird, wird state ein schlichtes Objekt

Before: CounterViewModel {value: 5} 
After: Object {value: 6} 

Wie reparierst du das?

Sie haben ein einfaches Objekt als state verwenden und Sie müssen sie behandeln wie ein unveränderlich Objekt.

var Counter = React.createClass({ 
    getInitialState: function() { 
     return {counter: new CounterViewModel()} 
    }, 

    sendTick: function() { 
     var stateObj = Object.assign(new CounterViewModel(),this.state.counter); // Deep clone 
     stateObj.tick(); 
     this.setState({counter: stateObj}); 
    }, 

    render: function() { 
     console.log(this.state); 
     return <div> 
      {this.state.counter.value} 
      <button onClick={this.sendTick}>Increase</button> 
     </div> 
    } 


}); 

working jsfiddle

+0

Ich empfehle, die änderbare Zählerinstanz nicht direkt im Komponentenstatus zu speichern. Kopieren Sie stattdessen die 'value' -Eigenschaft des Zählers in den Komponentenstatus – naomik

+0

Das ist auch in Ordnung!Aber das wird nicht zur dritten Frage passen, die lautet: "Wie kann ich komplexe Logik in das Zustandsobjekt von React einfügen" :-P – QoP

2

QoP korrekt ist, Reaktion erwartet, dass der Staat ein Vanille-JavaScript-Objekt zu sein. Wo meine Antwort von ihrer abweicht, ist, dass ich die CounterViewModel Instanz nicht in den Zustand der Komponente kopiere.

Es ist ziemlich gunky mit der separaten Klasse wie du bist, so dass die Interaktion damit ein wenig hässlich sein wird. Und warum verwenden Sie die Syntax class für Ihren anderen Code, aber die alte React-Syntax für die React-Komponente?

import {Component} from 'react' 

class Counter extends Component { 
    constructor (props) { 
    // call parent constructor 
    super(props) 
    // create an instance of counter view model 
    // save it as a normal instance property (not the same as `this.props`) 
    this.counter = new CounterViewModel() 
    // set initial state using the instance's counter value 
    // do NOT copy the counter object directly to state 
    this.state = { counter: this.counter.value } 
    } 
    sendTick (event) { 
    // mutate your counter object 
    this.counter.tick() 
    // set the component state with the counter's value 
    this.setState({ counter: this.counter.value }) 
    } 
    render() { 
    return (
     <div> 
     {this.state.value} 
     // note the slight difference in the way I set onClick 
     // read more: https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding 
     <button onClick={e=> this.sendTick(e)}>Increase</button> 
     </div> 
    ) 
    } 
} 

export default Counter