ReactJS durumu ve prop


121

Bu, cevap verilebilir ve düşünülmüş arasındaki çizgiyi aşıyor olabilir, ancak karmaşıklık büyüdükçe ve bir yöne ihtiyaç duydukça bir ReactJS bileşeninin nasıl yapılandırılacağı konusunda ileri geri gidiyorum.

AngularJS'den geliyorum, modelimi bileşene bir özellik olarak geçirmek ve bileşenin modeli doğrudan değiştirmesini istiyorum. Veya modeli çeşitli stateözelliklere ayırmalı ve yukarı akışa geri gönderirken tekrar birlikte mi derlemeliyim? ReactJS yolu nedir?

Bir blog yazısı düzenleyici örneğini ele alalım. Modeli doğrudan değiştirmeye çalışmak şuna benzer:

var PostEditor = React.createClass({
  updateText: function(e) {
    var text = e.target.value;
    this.props.post.text = text;
    this.forceUpdate();
  },
  render: function() {
    return (
      <input value={this.props.post.text} onChange={this.updateText}/>
      <button onClick={this.props.post.save}/>Save</button>
    );
  }
});

Yanlış görünüyor.

textModel özelliğimizi yapmanın stateve aşağıdaki gibi kaydetmeden önce modele geri derlemenin daha çok React yolu mu?

var PostEditor = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },
  componentWillMount: function() {
    this.setState({
      text: this.props.post.text
    });
  },
  updateText: function(e) {
    this.setState({
      text: e.target.value
    });
  },
  savePost: function() {
    this.props.post.text = this.state.text;
    this.props.post.save();
  },
  render: function() {
    return (
      <input value={this.state.text} onChange={this.updateText}/>
      <button onClick={this.savePost}/>Save</button>
    );
  }
});

Bu bir çağrı gerektirmez this.forceUpdate(), ancak model büyüdükçe (bir gönderinin bir yazarı, konusu, etiketleri, yorumları, derecelendirmeleri vb. Olabilir) bileşen gerçekten karmaşıklaşmaya başlar.

ReactLink ile ilk yöntem izlenecek yol mu?

Yanıtlar:


64

İkinci yaklaşımınız daha çok benziyor. React, değerleri ve uygulamanızda nasıl aktığını önemsediği için modelleri önemsemez . İdeal olarak, post modeliniz kökte tek bir bileşende depolanır. Daha sonra her biri modelin parçalarını tüketen alt bileşenler oluşturursunuz.

Geri aramaları verilerinizi değiştirmesi gereken alt bileşenlere aktarabilir ve bunları alt bileşenden çağırabilirsiniz.

This.props veya this.state'i doğrudan değiştirmek iyi bir fikir değildir, çünkü React değişiklikleri algılayamayacaktır. Bunun nedeni, React'in değişip değişmediğini belirlemek için post prop'unuzla sığ bir karşılaştırma yapmasıdır.

Bu jsfiddle'ı verilerin bir dış bileşenden bir iç bileşene nasıl akabileceğini göstermek için yaptım .

handleClickYöntem, Şekil (im) düzgün bir güncelleme durumuna 3 yolu:

var Outer = React.createClass({

  getInitialState: function() {
    return {data: {value: 'at first, it works'}};
  },

  handleClick: function () {

    // 1. This doesn't work, render is not triggered.
    // Never set state directly because the updated values
    // can still be read, which can lead to unexpected behavior.

    this.state.data.value = 'but React will never know!';

    // 2. This works, because we use setState

    var newData = {value: 'it works 2'};
    this.setState({data: newData});

    // 3. Alternatively you can use React's immutability helpers
    // to update more complex models.
    // Read more: http://facebook.github.io/react/docs/update.html

    var newState = React.addons.update(this.state, {
      data: {value: {$set: 'it works'}}
    });
    this.setState(newState);
 },

  render: function() {
      return <Inner data={this.state.data} handleClick={this.handleClick} />;
  }
});

Ama kendi durum manipülasyon işlevlerine sahip opak bir modelimiz varsa ne yapmalıyız? Örneğin, bir textalan yerine setText doğrulama ve diğer bazı şeyler yapan bir yöntemimiz olduğunu varsayalım . setTextSafsa ve modelin yepyeni bir örneğini döndürdüğünde çalışan yöntemin (2) çalıştığını görebiliyorum . Ancak, setTextsadece iç durumu güncellerse, yine de aramamız gerekir forceUpdate, değil mi?
hugomg

1
Evet, arayabilirsin forceUpdate, ama o noktada React'ten "sızıyorsun". setState()Yeniden oluşturmaları manuel olarak tetiklemekten kaçınmak için opak modele bir geri arama iletmek daha iyi olabilir .
jxg

Hala tam olarak anladığımdan emin değilim. Yani, verileri değiştirmeyi amaçlayan herhangi bir bileşenin, aktarılan nesnelerin derin bir kopyasını yapması gerekir mi? Daha sonra orijinal verileri değiştirmemek için bu kopyayı değiştirip yukarı gönder? Sonunda değişiklik, ele alındığı yer olan köke kadar akacak ve tüm uygulama yeniden oluşturulacak mı? Bu doğru mu?
nicholas

97

2016'nın Güncellenmesi: React değiştirildi ve "props vs state" açıklaması çok basit hale geldi. Bir bileşenin verileri değiştirmesi gerekiyorsa - bir duruma, aksi takdirde props'a koyun. Çünkü sahne donanımları artık salt okunur .

Sahne ve durum arasındaki tam fark nedir?

Burada iyi bir açıklama bulabilirsiniz (tam sürüm)

Sahne ve durumu değiştirme


1
aslında setProps () bir bileşenin içindeki nesneleri değiştirebilir ve yeniden oluşturmayı tetikleyebilir
WaiKit Kung

2
setPropskullanımdan kaldırıldı ve kullanılmamalıdır. Değiştirme, bileşeni yeniden işlemek ve React'in farklılıkları ele almasına izin vermektir.
jdmichal


35

React doc'tan

aksesuarlar değişmezdir: ebeveynden aktarılırlar ve ebeveyn tarafından "sahiplenirler". Etkileşimleri uygulamak için, bileşene değiştirilebilir durum ekliyoruz. this.state bileşene özeldir ve this.setState () çağrılarak değiştirilebilir. Durum güncellendiğinde, bileşen kendini yeniden oluşturur.

Kaynaktan TrySpace : sahne (ya da durum) (SetProps / setState veya ebeveyn ile) güncellendiklerinde bileşeni hem de yeniden oluşturur.


16

React'te Düşünme'den bir okuma :

Her birini gözden geçirelim ve hangisinin durum olduğunu bulalım. Her bir veri parçası hakkında üç soru sorun:

  1. Sahne aracılığıyla bir ebeveynden mi aktarılıyor? Eğer öyleyse, muhtemelen devlet değildir.
  2. Zamanla değişiyor mu? Değilse, muhtemelen devlet değildir.

  3. Bunu bileşeninizdeki başka herhangi bir duruma veya desteğe göre hesaplayabilir misiniz? Eğer öyleyse, devlet değil.


13

Sorunuza cevap verip vermediğimden emin değilim, ancak özellikle büyük / büyüyen bir uygulamada Konteyner / Bileşen modelinin inanılmaz derecede iyi çalıştığını gördüm.

Esasen iki React bileşeniniz var:

  • stil ve DOM etkileşimi ile ilgilenen "saf" bir görüntü bileşeni;
  • dış verilere erişme / kaydetme, durumu yönetme ve görüntüleme bileşenini işleme ile ilgilenen bir kap bileşeni.

Misal

NB Bu örnek, bu kadar basit bir durum için oldukça ayrıntılı olduğundan, bu modelin faydalarını göstermek için muhtemelen çok basittir.

/**
 * Container Component
 *
 *  - Manages component state
 *  - Does plumbing of data fetching/saving
 */

var PostEditorContainer = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },

  componentWillMount: function() {
    this.setState({
      text: getPostText()
    });
  },

  updateText: function(text) {
    this.setState({
      text: text
    });
  },

  savePost: function() {
    savePostText(this.state.text);
  },

  render: function() {
    return (
      <PostEditor
        text={this.state.text}
        onChange={this.updateText.bind(this)}
        onSave={this.savePost.bind(this)}
      />
    );
  }
});


/**
 * Pure Display Component
 *
 *  - Calculates styling based on passed properties
 *  - Often just a render method
 *  - Uses methods passed in from container to announce changes
 */

var PostEditor = React.createClass({
  render: function() {
    return (
      <div>
        <input type="text" value={this.props.text} onChange={this.props.onChange} />
        <button type="button" onClick={this.props.onSave} />
      </div>
    );
  }
});

Yararları

Görüntü mantığını ve veri / durum yönetimini ayrı tutarak, aşağıdaki özelliklere sahip yeniden kullanılabilir bir ekran bileşenine sahip olursunuz:

  • react-component-oyun alanı gibi bir şey kullanılarak farklı sahne setleriyle kolayca yinelenebilir
  • farklı davranışlar için farklı bir kapla sarılabilir (veya uygulamanızın daha büyük parçalarını oluşturmak için diğer bileşenlerle birleştirilebilir)

Ayrıca tüm harici iletişimle ilgilenen bir konteyner bileşeniniz de var. Bu, daha sonra ciddi değişiklikler yaparsanız verilerinize erişme şekliniz konusunda esnek olmanızı kolaylaştıracaktır *.

Bu model aynı zamanda birim testleri yazmayı ve uygulamayı çok daha kolay hale getirir.

Büyük bir React uygulamasını birkaç kez yineledikten sonra, bu kalıbın, özellikle hesaplanmış stiller veya karmaşık DOM etkileşimleri olan daha büyük bileşenleriniz olduğunda işleri nispeten ağrısız tuttuğunu gördüm.

* Akı modelini okuyun ve bu cevaba büyük ölçüde ilham veren Marty.js'ye bir göz atın (ve son zamanlarda çok kullanıyorum) Redux (ve react-redux ), bu modeli çok iyi uyguladı.

Bunu 2018 veya sonrasında okuyanlar için not:

React, bu cevabın yazılmasından bu yana, özellikle Hooks'un tanıtılmasıyla, epey gelişti . Bununla birlikte, bu örnekteki temel durum yönetimi mantığı aynı kalır ve daha da önemlisi, durumunuzu ve sunum mantığınızı ayrı tutmaktan elde ettiğiniz faydalar yine aynı şekillerde geçerlidir.


0

Sanırım Facebook'un bu bağlantıda zaten açıkladığı bir anti-model kullanıyorsunuz

İşte bulduğunuz şey:

React.createClass({
  getInitialState: function() {
    return { value: { foo: 'bar' } };
  },

  onClick: function() {
    var value = this.state.value;
    value.foo += 'bar'; // ANTI-PATTERN!
    this.setState({ value: value });
  },

  render: function() {
    return (
      <div>
        <InnerComponent value={this.state.value} />
        <a onClick={this.onClick}>Click me</a>
      </div>
    );
  }
});

İç bileşen ilk kez işlendiğinde, prop değeri olarak {foo: 'bar'} olacaktır. Kullanıcı bağlantıya tıklarsa, üst bileşenin durumu {değer: {foo: 'barbar'}} olarak güncellenecek ve iç bileşenin yeniden oluşturma sürecini tetikleyecek ve şu şekilde {foo: 'barbar'} alacak pervane için yeni değer.

Sorun, üst ve iç bileşenlerin aynı nesneye bir referansı paylaşması nedeniyle, nesne onClick işlevinin 2. satırında mutasyona uğradığında, iç bileşenin sahip olduğu pervane değişecektir. Dolayısıyla, yeniden oluşturma işlemi başladığında ve shouldComponentUpdate çağrıldığında, this.props.value.foo nextProps.value.foo'ya eşit olacaktır, çünkü aslında this.props.value, nextProps.value ile aynı nesneye başvurur.

Sonuç olarak, pervanedeki değişikliği kaçıracağımız ve yeniden oluşturma sürecini kısa devre yapacağımız için, UI 'bar'dan' barbar'a güncellenmeyecektir.


Lütfen Innercomponentskodu da gönderebilir misiniz ?
Abdullah Khan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.