ReactJS İletişim kuran iki bileşen


321

ReactJS ile yeni başladım ve sahip olduğum bir soruna biraz takıldım.

Uygulamam aslında filtreleri içeren bir liste ve düzeni değiştirmek için bir düğme. : Şu anda üç bileşeni kullanıyorum <list />, < Filters />ve <TopBar />şimdi açıkçası ben ayarlarını değiştirmek zaman, < Filters />ben de bazı yöntem tetiklemek istiyorum <list />görüşümü güncellemek için.

Bu 3 bileşenin birbiriyle nasıl etkileşime girmesini sağlayabilirim veya yalnızca değişiklik yapabileceğim bir çeşit küresel veri modeline ihtiyacım var mı?


Her üç kardeş bileşen mi yoksa biri diğerinin içinde mi?
pgreen2

Üç bileşenin hepsi, uygulamanızı yeniden düzenledim, böylece artık hepsinin veriyi sağlayabilecek aynı ebeveynleri var.
woutr_be

4
Akı veya pubsub desenini kullanabileceğiniz yer burasıdır. Tepki belgelerindeki dokümanlara dayanarak, belirsiz bir cümle bırakıyorlar: "Ebeveyn-çocuk ilişkisi olmayan iki bileşen arasındaki iletişim için kendi global olay sisteminizi kurabilirsiniz." facebook.github.io/react/tips/…
BingeBoy

@BingeBoy haklı Flux , birçok bileşen tarafından veri paylaşımı, veri paylaşımı sorununu çözebilen tepki uygulamaları yazmanın harika bir yoludur.
Ankit Patial

Yanıtlar:


318

En iyi yaklaşım, bu bileşenleri nasıl düzenlemeyi planladığınıza bağlıdır. Şu anda akla gelen birkaç örnek senaryo:

  1. <Filters /> bir alt bileşenidir <List />
  2. Hem <Filters />ve <List />ana bileşenin çocukları
  3. <Filters />ve <List />tamamen ayrı kök bileşenlerinde yaşarlar.

Düşünmediğim başka senaryolar da olabilir. Sizinkiler bunlara uymuyorsa bana bildirin. İlk iki senaryoyu nasıl ele aldığımla ilgili çok kabaca örnekler:

Senaryo # 1

Sen bir işleyici geçebileceği <List />kadar <Filters />o andan denebilir, onChangeşimdiki değeri ile listeye filtre olay.

# 1 için JSFiddle →

/** @jsx React.DOM */

var Filters = React.createClass({
  handleFilterChange: function() {
    var value = this.refs.filterInput.getDOMNode().value;
    this.props.updateFilter(value);
  },
  render: function() {
    return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
  }
});

var List = React.createClass({
  getInitialState: function() {
    return {
      listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
      nameFilter: ''
    };
  },
  handleFilterUpdate: function(filterValue) {
    this.setState({
      nameFilter: filterValue
    });
  },
  render: function() {
    var displayedItems = this.state.listItems.filter(function(item) {
      var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
      return (match !== -1);
    }.bind(this));

    var content;
    if (displayedItems.length > 0) {
      var items = displayedItems.map(function(item) {
        return <li>{item}</li>;
      });
      content = <ul>{items}</ul>
    } else {
      content = <p>No items matching this filter</p>;
    }

    return (
      <div>
        <Filters updateFilter={this.handleFilterUpdate} />
        <h4>Results</h4>
        {content}
      </div>
    );
  }
});

React.renderComponent(<List />, document.body);

Senaryo # 2

Senaryo # 1'e benzer, ancak üst bileşen, işleyici işlevinden aşağıya aktarılan bileşen olacaktır <Filters />ve filtrelenmiş listeyi iletecektir <List />. Ben o kadar iyi ayırır beri bu yöntemi gibi <List />gelen <Filters />.

# 2 için JSFiddle →

/** @jsx React.DOM */

var Filters = React.createClass({
  handleFilterChange: function() {
    var value = this.refs.filterInput.getDOMNode().value;
    this.props.updateFilter(value);
  },
  render: function() {
    return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
  }
});

var List = React.createClass({
  render: function() {
    var content;
    if (this.props.items.length > 0) {
      var items = this.props.items.map(function(item) {
        return <li>{item}</li>;
      });
      content = <ul>{items}</ul>
    } else {
      content = <p>No items matching this filter</p>;
    }
    return (
      <div className="results">
        <h4>Results</h4>
        {content}
      </div>
    );
  }
});

var ListContainer = React.createClass({
  getInitialState: function() {
    return {
      listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
      nameFilter: ''
    };
  },
  handleFilterUpdate: function(filterValue) {
    this.setState({
      nameFilter: filterValue
    });
  },
  render: function() {
    var displayedItems = this.state.listItems.filter(function(item) {
      var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
      return (match !== -1);
    }.bind(this));

    return (
      <div>
        <Filters updateFilter={this.handleFilterUpdate} />
        <List items={displayedItems} />
      </div>
    );
  }
});

React.renderComponent(<ListContainer />, document.body);

Senaryo # 3

Bileşenler herhangi bir üst-alt öğe ilişkisi arasında iletişim kuramadığında, belgeler genel bir olay sistemi kurulmasını önerir .


6
Gibi bir işleve: 2. ile güzel bir şey onlar sadece her bileşenine bir pervane geçen bir ebeveyn güvenmek olduğunu updateFilteriçin <Filters />ve gibi bir diziye itemsiçin <List />. Bu alt bileşenleri, birlikte veya tek başına farklı davranışlara sahip diğer ebeveynlerde kullanabilirsiniz. Örneğin, dinamik bir liste görüntülemek istiyor ancak filtrelemeye ihtiyacınız yoksa.
Michael LaCroix

2
@woutr_be İhtiyacınıza uygun olup olmadığından emin değilim, ancak benzer bir durumda geri döndüğümüzde, alt ve üst bileşenler arasında iletişim kurmak için aşağıdaki iki işlevi kullandık: - listenTo: function (eventName, eventCallback) {$ ( window.document) .bind (eventName, eventCallback);} triggerEvent: function (eventName, params) {$ .event.trigger (eventName, params);} Umarım yardımcı olur! (üzgünüm daha iyi biçimlendiremedi)
5122014009

29
Senaryo 3 için önerilen bir yaklaşım var mı? Özel sentetik etkinlikler oluşturarak bununla ilgili herhangi bir belge veya örnek var mı? Ana dokümanlarda hiçbir şey bulamadım.
pwray

1
Senaryo 2. yapar ... anlamda bir çok dek sen (keşke, Düzen) tasarımı tehlikeye zorunda - o zaman bir EventHub / PubSub için gerekli gerçekleştirmek.
Cody

4
Senaryo # 3 bağlantısı öldü ve şimdi ilgisiz bir Tepki dokümanları sayfasına yönlendiriliyor.
beporter

170

Bileşenlerin iletişim kurmasının birden çok yolu vardır. Bazıları kullanıcı tabanınıza uygun olabilir. Bilmem için yararlı bulduğum bazılarının listesi.

Tepki

Ebeveyn / Çocuk doğrudan iletişimi

const Child = ({fromChildToParentCallback}) => (
  <div onClick={() => fromChildToParentCallback(42)}>
    Click me
  </div>
);

class Parent extends React.Component {
  receiveChildValue = (value) => {
    console.log("Parent received value from child: " + value); // value is 42
  };
  render() {
    return (
      <Child fromChildToParentCallback={this.receiveChildValue}/>
    )
  }
}

Burada alt bileşen, üst tarafından değeri olan bir geri aramayı çağırır ve üst, üst öğedeki çocuklar tarafından sağlanan değeri alabilir.

Uygulamanızın bir özelliğini / sayfasını oluşturursanız, geri çağrıları / durumu ( containerveya olarak da adlandırılır smart component) yöneten tek bir ebeveyne sahip olmak ve tüm çocukların vatansız olması daha iyidir , yalnızca ebeveynlere bir şeyler bildirir. Bu şekilde ebeveynin durumunu ihtiyaç duyan herhangi bir çocuğa kolayca "paylaşabilirsiniz".


bağlam

Reaksiyon Bağlamı, durumu bileşen hiyerarşinizin kökünde tutmaya izin verir ve bu durumu, her ara bileşene sahne aktarmak zorunda kalmadan kolayca çok derin yuvalanmış bileşenlere enjekte edebilir.

Şimdiye kadar bağlam deneysel bir özellikti, ancak React 16.3'te yeni bir API mevcut.

const AppContext = React.createContext(null)

class App extends React.Component {
  render() {
    return (
      <AppContext.Provider value={{language: "en",userId: 42}}>
        <div>
          ...
          <SomeDeeplyNestedComponent/>
          ...
        </div>
      </AppContext.Provider>
    )
  }
};

const SomeDeeplyNestedComponent = () => (
  <AppContext.Consumer>
    {({language}) => <div>App language is currently {language}</div>}
  </AppContext.Consumer>
);

Tüketici render prop / children işlev modelini kullanıyor

Daha fazla ayrıntı için bu blog yayınına bakın.

React 16.3'ten önce, oldukça benzer API sunan ve eski bağlam API'sını kullanan tepki yayınını kullanmanızı öneririm .


Portallar

Normal üst / alt öğedeki gibi basit işlevlerle iletişim kurmalarını sağlamak için 2 bileşeni birbirine yakın tutmak istediğinizde portal kullanın, ancak bu 2 bileşenin DOM'de üst / alt ilişkisi olmasını istemezsiniz, çünkü ima ettiği görsel / CSS kısıtlamaları (z-endeksi, opaklık ... gibi).

Bu durumda bir "portal" kullanabilirsiniz. Genellikle modals , popup, tooltips için kullanılan portalları kullanan farklı reaksiyon kütüphaneleri vardır ...

Aşağıdakileri göz önünde bulundur:

<div className="a">
    a content
    <Portal target="body">
        <div className="b">
            b content
        </div>
    </Portal>
</div>

İçinde işlendiğinde aşağıdaki DOM üretilebilir reactAppContainer:

<body>
    <div id="reactAppContainer">
        <div className="a">
             a content
        </div>
    </div>
    <div className="b">
         b content
    </div>
</body>

Daha fazla ayrıntı burada


yuvalar

Bir yerde bir yuva tanımlarsınız ve daha sonra yuvayı oluşturma ağacınızın başka bir yerinden doldurursunuz.

import { Slot, Fill } from 'react-slot-fill';

const Toolbar = (props) =>
  <div>
    <Slot name="ToolbarContent" />
  </div>

export default Toolbar;

export const FillToolbar = ({children}) =>
  <Fill name="ToolbarContent">
    {children}
  </Fill>

Bu, portallara biraz benzer, ancak doldurulmuş içerik tanımladığınız bir yuvada oluşturulurken, portallar genellikle yeni bir dom düğümü oluşturur (genellikle belgenin çocuklarıdır.)

Reaksiyon yuvası doldurma kitaplığını kontrol edin


Etkinlik otobüsü

React belgelerinde belirtildiği gibi :

Ebeveyn-çocuk ilişkisi olmayan iki bileşen arasındaki iletişim için kendi global olay sisteminizi kurabilirsiniz. ComponentDidMount () içindeki olaylara abone olun, componentWillUnmount () öğesinden çıkın ve bir olay aldığınızda setState () öğesini çağırın.

Bir olay veri yolu kurmak için kullanabileceğiniz birçok şey vardır. Sadece bir dizi dinleyici oluşturabilirsiniz ve etkinlik yayınlandığında tüm dinleyiciler etkinliği alır. Veya EventEmitter veya PostalJs gibi bir şey kullanabilirsiniz


Akı

Flux temel olarak bir olay veri yoludur, ancak olay alıcıları mağazalardır. Bu durum, durum React dışında yönetilmesi dışında temel olay veri yolu sistemine benzer

Orijinal Flux uygulaması, Olay kaynağı kullanımını hacky bir şekilde yapma girişimi gibi görünüyor.

Redux benim için olay kaynaklarından en yakın olan Flux uygulamasıdır, zaman yolculuğu yeteneği gibi olay kaynağı avantajlarının çoğuna fayda sağlar. React ile kesin olarak bağlantılı değildir ve diğer işlevsel görünüm kütüphaneleriyle de kullanılabilir.

Egghead'in Redux video eğitimi gerçekten güzel ve dahili olarak nasıl çalıştığını açıklıyor (gerçekten basit).


imleçler

İmleçler ClojureScript / Om'dan geliyor ve React projelerinde yaygın olarak kullanılıyor. React dışındaki durumu yönetmeye izin verir ve bileşen ağacı hakkında hiçbir şey bilmeye gerek kalmadan, birden fazla bileşenin devletin aynı bölümüne okuma / yazma erişimine izin verir.

ImmutableJS , React-ursors ve Omniscient gibi birçok uygulama mevcut

Edit 2016 : İnsanlar imleçlerin daha küçük uygulamalar için iyi çalıştığını kabul ediyor gibi görünüyor, ancak karmaşık uygulamalarda iyi ölçeklenmiyor. Om Next'in artık imleçleri yok (konsepti başlangıçta tanıtan Om olsa da)


Karaağaç mimarisi

Elm mimarisi tarafından kullanılmak üzere önerilen bir mimaridir Elm dili . Elm ReactJS olmasa bile, Elm mimarisi React'te de yapılabilir.

Redux'nun yazarı Dan Abramov, React kullanarak Elm mimarisinin bir uygulamasını yaptı.

Hem Redux hem de Elm gerçekten harika ve ön uçtaki olay kaynağı kavramlarını güçlendirmeye eğilimlidirler, her ikisi de zaman yolculuğu hata ayıklama, geri alma / yineleme, tekrar oynatma ...

Redux ve Elm arasındaki temel fark, Elm'in devlet yönetimi konusunda çok daha katı olma eğilimindedir. Elm'de yerel bileşen durumunuz veya takma / çıkarma kancalarınız olamaz ve tüm DOM değişikliklerinin genel durum değişiklikleri tarafından tetiklenmesi gerekir. Karaağaç mimarisi TÜM durumu tek bir değişmez nesne içinde işlemeye izin veren ölçeklenebilir bir yaklaşım önerirken, Redux sizi tek bir değişmez nesne içinde devletin EN ÇOK ele almaya davet eden bir yaklaşım önerir.

Elm'in kavramsal modeli çok zarif olsa da ve mimari büyük uygulamalarda iyi ölçeklenmesine izin verirken, pratikte zor olabilir veya bir girdiyi monte ettikten sonra odaklanmak veya mevcut bir kütüphaneyle entegre etmek gibi basit görevleri yerine getirmek için daha fazla kaynak içerebilir. zorunlu bir arayüz ile (yani JQuery eklentisi). İlgili konu .

Ayrıca, Elm mimarisi daha fazla kod kaynatma plakası içerir. Yazmak o kadar karmaşık ya da karmaşık değil ama Elm mimarisinin statik olarak yazılmış diller için daha uygun olduğunu düşünüyorum.


CTP

RxJS, BaconJS veya Kefir gibi kütüphaneler, bileşenler arasındaki iletişimi sağlamak için FRP akışları üretmek için kullanılabilir.

Örneğin Rx-React'i deneyebilirsiniz

Bence bu kütüphaneleri kullanmak, ELM dilinin sinyallerle sunduklarını kullanmaya oldukça benzer .

CycleJS çerçevesi ReactJS kullanmaz, ancak vdom kullanır . Elm mimarisi ile birçok benzerlik paylaşır (ancak gerçek hayatta kullanımı daha kolaydır, çünkü vdom kancalarına izin verir) ve işlevler yerine RxJ'leri yaygın olarak kullanır ve FRP ile kullanmak istiyorsanız iyi bir ilham kaynağı olabilir. Tepki. CycleJs Egghead videoları , nasıl çalıştığını anlamak için iyi.


CSP

CSP (Sıralı İşlemleri İletişim Etme) şu anda popülerdir (çoğunlukla Go / goroutines ve core.async / ClojureScript nedeniyle), ancak JS-CSP ile javascript'te de kullanabilirsiniz .

James Long, React ile nasıl kullanılabileceğini açıklayan bir video yaptı .

Sagalar

Destan, DDD / EventSourcing / CQRS dünyasından gelen ve ayrıca "süreç yöneticisi" olarak adlandırılan bir arka plan konseptidir. Bu tarafından popüler olan Redux-Saga çoğunlukla yan etkiler (örneğin, API çağrıları vs.) depolanması için Redux-thunk için bir yedek olarak, proje. Çoğu insan şu anda sadece yan etkilere hizmet ettiğini düşünüyor, ancak aslında daha çok bileşenleri ayırmakla ilgili.

Bu, tamamen yeni bir iletişim sisteminden çok bir Flux mimarisine (veya Redux) iltifattır, çünkü destan sonunda Flux eylemleri yayar. Fikir şudur: widget1 ve widget2'niz varsa ve bunların ayrıştırılmasını istiyorsanız, widget1'den eylem hedefleme widget2'sini tetikleyemezsiniz. Widget1'i yalnızca kendisini hedefleyen eylemleri gerçekleştirirsiniz ve destan, widget1 eylemlerini dinleyen ve widget2'yi hedefleyen eylemler gönderebilen bir "arka plan işlemidir". Efsane, 2 widget arasındaki bağlantı noktasıdır, ancak widget'lar ayrıştırılmış olarak kalır.

Eğer ilgileniyorsanız burada benim cevap bir göz atın


Sonuç

Bu farklı stilleri kullanarak aynı küçük uygulamanın bir örneğini görmek istiyorsanız, bu deponun dallarını kontrol edin .

Uzun vadede en iyi seçeneğin ne olduğunu bilmiyorum ama Flux'un olay kaynağı gibi görünmesini gerçekten çok seviyorum.

Olay kaynağı oluşturma kavramlarını bilmiyorsanız, bu çok pedagojik bloga bir göz atın: Veritabanını apache Samza ile ters çevirerek , Flux'un neden güzel olduğunu anlamak gerekir (ancak bu FRP için de geçerli olabilir) )

Topluluğun en umut verici Flux uygulamasının, sıcak yeniden yükleme sayesinde giderek daha verimli geliştirici deneyimine izin verecek olan Redux olduğunu kabul ettiğini düşünüyorum . Etkileyici canlı kodlama ala Bret Victor'un Prensipte İcat videosu mümkün!


2
Mükemmel cevap Seb!
Ali Gajani

7

Tamam, bunu yapmanın birkaç yolu var, ancak sadece Redux kullanarak mağaza kullanmaya odaklanmak istiyorum, bu da hayatınızı sadece bu durumda hızlı bir çözüm sunmak yerine bu durumlar için çok daha kolay hale getiriyor, saf React kullanmak karışıklığa neden olacak gerçek büyük uygulama ve Bileşenler arasında iletişim uygulama büyüdükçe zorlaşıyor ...

Redux sizin için ne yapıyor?

Redux, uygulamanızdaki farklı yerlerde kullanılmak üzere verilere ihtiyaç duyduğunuzda kullanılabilecek yerel depolama gibidir ...

Temel olarak, Redux fikri başlangıçta akıdan gelir, ancak sadece bir mağaza oluşturarak tek bir gerçek kaynağına sahip olma kavramı dahil olmak üzere bazı temel değişikliklerle ...

Flux ve Redux arasındaki bazı farklılıkları görmek için aşağıdaki grafiğe bakın ...

Redux ve Akı

Uygulamanız Bileşenler arasında iletişime ihtiyaç duyuyorsa , baştan itibaren uygulamanıza Redux uygulamayı düşünün ...

Ayrıca, Redux Belgeleri'nden bu kelimeleri okumak aşağıdakilerle yardımcı olabilir:

Tek sayfa JavaScript uygulamaları için gereksinimler gittikçe karmaşıklaştıkça, kodumuzun her zamankinden daha fazla durumu yönetmesi gerekir . Bu durum, sunucu yanıtlarını ve önbelleğe alınmış verileri ve ayrıca henüz sunucuda kalıcı olarak bulunmayan yerel olarak oluşturulan verileri içerebilir. Aktif rotaları, seçilen sekmeleri, eğiricileri, sayfalama kontrollerini vb. Yönetmemiz gerektiğinden, kullanıcı arayüzü durumu karmaşıklık açısından da artmaktadır.

Bu sürekli değişen durumu yönetmek zor. Bir model başka bir modeli güncelleyebiliyorsa, görünüm başka bir modeli güncelleyen bir modeli güncelleyebilir ve bu da başka bir görünümün güncellenmesine neden olabilir. Bir noktada, durumunun ne zaman, neden ve nasıl olduğu üzerinde kontrolü kaybettiğiniz için uygulamanızda neler olduğunu artık anlayamazsınız. Bir sistem opak ve deterministik olmadığında, hataları yeniden oluşturmak veya yeni özellikler eklemek zordur.

Bu yeterince kötü değilmiş gibi, yeni gereksinimlerin ön uç ürün geliştirmede yaygınlaştığını düşünün. Geliştiriciler olarak, rota geçişleri gerçekleştirmeden önce iyimser güncellemeler, sunucu tarafı oluşturma, veri alma ve benzeri işlemleri yapmamız bekleniyor. Kendimizi daha önce hiç uğraşmamamız gereken bir karmaşıklığı yönetmeye çalışırken buluyoruz ve kaçınılmaz olarak şu soruyu soruyoruz: pes etme zamanı geldi mi? Cevap hayır.

İnsan zihninin akla getirmesi çok zor olan iki kavramı karıştırdığımız için bu karmaşıklığın üstesinden gelmek zordur: mutasyon ve asenkroniklik. Onlara Mentos ve Kola diyorum. Her ikisi de ayrılık açısından harika olabilir, ancak birlikte bir karmaşa yaratırlar. React gibi kütüphaneler, bu sorunu görünüm katmanında hem eşzamansızlığı hem de doğrudan DOM manipülasyonunu kaldırarak çözmeye çalışır. Ancak, verilerinizin durumunu yönetmek size kalmış. İşte bu noktada Redux devreye giriyor.

İzinden Flux, CQRS ve Etkinlik Satınalma , güncellemeler olacağını nasıl ve ne zaman belirli sınırlamalar empoze ederek devlet mutasyonlar öngörülebilir hale getirmek için Redux girişimleri . Bu kısıtlamalar, Redux'un üç ilkesine yansımaktadır.


Redux nasıl yardımcı olabilir ? datepicker(bileşen olarak) için bir modum varsa ve bu bileşen aynı sayfada yaşayan birden çok bileşenden yüklenebilirse, datepickerbileşen hangi Eylemin redux'a gönderileceğini nasıl bilebilir ? Bu bir harekete bağlayan Sorunun özü, bir bileşen başka bto ve NOT başka bileşen . ( datepickerkendisinin modal bileşen içinde derin, derin bir bileşen olduğunu göz önünde bulundurun )
vsync

@vsync, reudx'u tek bir statik değer olarak düşünmez, redux aslında nesneler, diziler olabilir ... böylece bunları nesne veya dizi veya mağazanızdaki herhangi bir şey olarak kaydedebilirsiniz, mapdispatchtoprops olabilir ve her biri nesne dizisine kaydedilebilir like: [{name: "picker1", değer: "01/01/1970"}, {name: "picker2", değer: "01/01/1980"}] ve ardından üst öğede mapstatetoprops kullanın ve her bileşen veya istediğiniz yerde, sorunuzu cevaplayıp yanıtlamadığından emin değilsiniz, ancak kodu görmeden ... ayrı gruplardalarsa, daha fazla ayrıntıyla da itiraz edebilirsiniz ... ama hepsi nasıl gruplandırmak istediğinize bağlı onları ..
Alireza

Soru reduxne hakkında ve ne saklayabileceğiniz değil , eylemi nasıl tetikleyeceklerine derinlemesine nasıl aktaracağınız. Derin bir bileşen hangi TAMAMEN'nin tetiklenmesi gerektiğini nasıl bilir ? çünkü örnekte, senaryoya bağlı olarak belirli bir redüktöre tetiklenmesi gereken genel bir bileşen verdim, bu nedenle herhangi bir bileşen için bir tarih seçici modal kullanılabileceğinden farklı redüktör olabilir.
vsync

5

Bunu ben böyle hallettim.
Diyelim ki Ay için bir <select> ve Day için bir <select> var . Gün sayısı seçilen aya göre değişir.

Her iki liste de üçüncü panel olan sol panele aittir. Hem <select> hem de leftPanel <div> çocuklarıdır. LeftPanel bileşenindeki
geri çağrıları ve işleyicileri olan bir oyundur .

Test etmek için kodu iki ayrı dosyaya kopyalayın ve index.html dosyasını çalıştırın . Ardından bir ay seçin ve gün sayısının nasıl değiştiğini görün.

dates.js

    /** @jsx React.DOM */


    var monthsLength = [0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    var MONTHS_ARR = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];

    var DayNumber = React.createClass({
        render: function() {
            return (
                <option value={this.props.dayNum}>{this.props.dayNum}</option>
            );
        }
    });

    var DaysList = React.createClass({
        getInitialState: function() {
            return {numOfDays: 30};
        },
        handleMonthUpdate: function(newMonthix) {
            this.state.numOfDays = monthsLength[newMonthix];
            console.log("Setting days to " + monthsLength[newMonthix] + " month = " + newMonthix);

            this.forceUpdate();
        },
        handleDaySelection: function(evt) {
            this.props.dateHandler(evt.target.value);
        },
        componentDidMount: function() {
            this.props.readyCallback(this.handleMonthUpdate)
        },
        render: function() {
            var dayNodes = [];
            for (i = 1; i <= this.state.numOfDays; i++) {
                dayNodes = dayNodes.concat([<DayNumber dayNum={i} />]);
            }
            return (
                <select id={this.props.id} onChange = {this.handleDaySelection}>
                    <option value="" disabled defaultValue>Day</option>
                        {dayNodes}
                </select>
                );
        }
    });

    var Month = React.createClass({
        render: function() {
            return (
                <option value={this.props.monthIx}>{this.props.month}</option>
            );
        }
    });

    var MonthsList = React.createClass({
        handleUpdate: function(evt) {
            console.log("Local handler:" + this.props.id + " VAL= " + evt.target.value);
            this.props.dateHandler(evt.target.value);

            return false;
        },
        render: function() {
            var monthIx = 0;

            var monthNodes = this.props.data.map(function (month) {
                monthIx++;
                return (
                    <Month month={month} monthIx={monthIx} />
                    );
            });

            return (
                <select id = {this.props.id} onChange = {this.handleUpdate}>
                    <option value="" disabled defaultValue>Month</option>
                        {monthNodes}
                </select>
                );
        }
    });

    var LeftPanel = React.createClass({
        dayRefresh: function(newMonth) {
            // Nothing - will be replaced
        },
        daysReady: function(refreshCallback) {
            console.log("Regisering days list");
        this.dayRefresh = refreshCallback;
        },
        handleMonthChange: function(monthIx) {
            console.log("New month");
            this.dayRefresh(monthIx);
        },
        handleDayChange: function(dayIx) {
            console.log("New DAY: " + dayIx);
        },
        render: function() {
            return(
                <div id="orderDetails">
                    <DaysList id="dayPicker" dateHandler={this.handleDayChange} readyCallback = {this.daysReady} />
                    <MonthsList data={MONTHS_ARR} id="monthPicker" dateHandler={this.handleMonthChange}  />
                </div>
            );
        }
    });



    React.renderComponent(
        <LeftPanel />,
        document.getElementById('leftPanel')
    );

Ve sol panel bileşeni index.html'yi çalıştırmak için HTML

<!DOCTYPE html>
<html>
<head>
    <title>Dates</title>

    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
    <script src="//fb.me/react-0.11.1.js"></script>
    <script src="//fb.me/JSXTransformer-0.11.1.js"></script>
</head>

    <style>

        #dayPicker {
            position: relative;
            top: 97px;
            left: 20px;
            width: 60px;
            height: 17px;
        }

        #monthPicker {
            position: relative;
            top: 97px;
            left: 22px;
            width: 95px;
            height: 17px;
        }

        select {
            font-size: 11px;
        }

    </style>


    <body>
        <div id="leftPanel">
        </div>

        <script type="text/jsx" src="dates.js"></script>

    </body>
</html>

örnek kodun% 80'ini silebilir ve yine de noktanızı koruyabilseydiniz, en iyisi olurdu. bu konu bağlamında CSS gösteren ilgisiz
vsync

3

Sorunun zaten yanıtlandığını gördüm, ancak daha fazla ayrıntı öğrenmek isterseniz, bileşenler arasında toplam 3 iletişim vakası var :

  • Durum 1: Ebeveyn-Çocuk iletişimi
  • Durum 2: Çocuk-Ebeveyn iletişimi
  • Durum 3: İlgili olmayan bileşenler (herhangi bir bileşenin herhangi bir bileşeni) iletişimi

1

Bir senaryo, bileşenlerin herhangi bir üst-alt ilişkisi arasında iletişim kuramadığı durumlarda @MichaelLaCroix'in yanıtını genişleten belgeler, genel bir olay sistemi oluşturulmasını önerir.

Yukarıdaki ilişki durumunda <Filters />ve <TopBar />bunlardan herhangi birine sahip değilseniz, basit bir küresel yayıcı şu şekilde kullanılabilir:

componentDidMount - Etkinliğe abone olun

componentWillUnmount - Etkinlikten çık

React.js ve EventSystem kodu

EventSystem.js

class EventSystem{

    constructor() {
        this.queue = {};
        this.maxNamespaceSize = 50;
    }

    publish(/** namespace **/ /** arguments **/) {
        if(arguments.length < 1) {
            throw "Invalid namespace to publish";
        }

        var namespace = arguments[0];
        var queue = this.queue[namespace];

        if (typeof queue === 'undefined' || queue.length < 1) {
            console.log('did not find queue for %s', namespace);
            return false;
        }

        var valueArgs = Array.prototype.slice.call(arguments);

        valueArgs.shift(); // remove namespace value from value args

        queue.forEach(function(callback) {
            callback.apply(null, valueArgs);
        });

        return true;
    }

    subscribe(/** namespace **/ /** callback **/) {
        const namespace = arguments[0];
        if(!namespace) throw "Invalid namespace";
        const callback = arguments[arguments.length - 1];
        if(typeof callback !== 'function') throw "Invalid callback method";

        if (typeof this.queue[namespace] === 'undefined') {
            this.queue[namespace] = [];
        }

        const queue = this.queue[namespace];
        if(queue.length === this.maxNamespaceSize) {
            console.warn('Shifting first element in queue: `%s` since it reached max namespace queue count : %d', namespace, this.maxNamespaceSize);
            queue.shift();
        }

        // Check if this callback already exists for this namespace
        for(var i = 0; i < queue.length; i++) {
            if(queue[i] === callback) {
                throw ("The exact same callback exists on this namespace: " + namespace);
            }
        }

        this.queue[namespace].push(callback);

        return [namespace, callback];
    }

    unsubscribe(/** array or topic, method **/) {
        let namespace;
        let callback;
        if(arguments.length === 1) {
            let arg = arguments[0];
            if(!arg || !Array.isArray(arg)) throw "Unsubscribe argument must be an array";
            namespace = arg[0];
            callback = arg[1];
        }
        else if(arguments.length === 2) {
            namespace = arguments[0];
            callback = arguments[1];
        }

        if(!namespace || typeof callback !== 'function') throw "Namespace must exist or callback must be a function";
        const queue = this.queue[namespace];
        if(queue) {
            for(var i = 0; i < queue.length; i++) {
                if(queue[i] === callback) {
                    queue.splice(i, 1); // only unique callbacks can be pushed to same namespace queue
                    return;
                }
            }
        }
    }

    setNamespaceSize(size) {
        if(!this.isNumber(size)) throw "Queue size must be a number";
        this.maxNamespaceSize = size;
        return true;
    }

    isNumber(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    }

}

NotificationComponent.js

class NotificationComponent extends React.Component {

    getInitialState() {
        return {
            // optional. see alternative below
            subscriber: null
        };
    }

    errorHandler() {
        const topic = arguments[0];
        const label = arguments[1];
        console.log('Topic %s label %s', topic, label);
    }

    componentDidMount() {
        var subscriber = EventSystem.subscribe('error.http', this.errorHandler);
        this.state.subscriber = subscriber;
    }

    componentWillUnmount() {
        EventSystem.unsubscribe('error.http', this.errorHandler);

        // alternatively
        // EventSystem.unsubscribe(this.state.subscriber);
    }

    render() {

    }
}

0

Ebeveyn - Çocuk ilişkisi olmasa bile böyle bir olasılık var ve bu Flux. Alt.JS (Alt-Container ile) adı verilen uygulama için oldukça iyi bir uygulama var.

Örneğin, Bileşen Ayrıntıları'nda nelerin ayarlandığına bağlı olan Kenar Çubuğuna sahip olabilirsiniz. Bileşen Kenar Çubuğu SidebarActions ve SidebarStore ile bağlanırken Ayrıntılar DetailsActions ve DetailsStore'dur.

O zaman AltContainer'ı şu şekilde kullanabilirsiniz:

<AltContainer stores={{
                    SidebarStore: SidebarStore
                }}>
                    <Sidebar/>
</AltContainer>

{this.props.content}

Hangi mağaza tutmak (iyi "mağaza" pervane yerine "mağaza" kullanabilirsiniz). Şimdi {this.props.content} OLABİLİR Rotaya bağlı olarak ayrıntılar. Diyelim ki / Ayrıntılar bizi bu görünüme yönlendiriyor. Ayrıntılar, örneğin işaretlenirse Kenar Çubuğu öğesini X'ten Y'ye değiştirecek bir onay kutusuna sahip olacaktır.

Teknik olarak aralarında hiçbir ilişki yoktur ve akı olmadan yapmak zor olacaktır. AMA BU İLE oldukça kolaydır.

Şimdi DetailsActions'a geçelim. Orada yaratacağız

class SiteActions {
constructor() {
    this.generateActions(
        'setSiteComponentStore'
    );
}

setSiteComponent(value) {
    this.dispatch({value: value});
}
}

ve Detaylar

class SiteStore {
constructor() {
    this.siteComponents = {
        Prop: true
    };

    this.bindListeners({
        setSiteComponent: SidebarActions.COMPONENT_STATUS_CHANGED
    })
}

setSiteComponent(data) {
    this.siteComponents.Prop = data.value;
}
}

Ve şimdi, bu sihrin başladığı yer.

Gördüğünüz gibi SidebarActions.ComponentStatusChanged içinde kullanılacak setSiteComponent IF kullanılacak bindListener var.

şimdi SidebarActions'ta

    componentStatusChanged(value){
    this.dispatch({value: value});
}

Böyle bir şeyimiz var. Bu nesneyi çağrı üzerine gönderir. Ve setSiteComponent deposunda kullanılacaksa çağrılır (örneğin onChange on Button sırasında bileşende kullanabilirsiniz)

Şimdi SidebarStore'da

    constructor() {
    this.structures = [];

    this.bindListeners({
        componentStatusChanged: SidebarActions.COMPONENT_STATUS_CHANGED
    })
}

    componentStatusChanged(data) {
    this.waitFor(DetailsStore);

    _.findWhere(this.structures[0].elem, {title: 'Example'}).enabled = data.value;
}

Şimdi burada görebileceksiniz, DetailsStore'u bekleyecek. Bunun anlamı ne? aşağı yukarı bu yöntemin kendisini güncellemeden önce DetailsStoreto güncellemesini beklemesi gerektiği anlamına gelir.

tl; dr One Store bir mağazadaki yöntemleri dinliyor ve bileşen deposundan kendi mağazasını güncelleyecek bir eylem tetikleyecek.

Umarım bir şekilde size yardımcı olabilir.


0

Bileşenler arasında iletişim kurma seçeneklerini keşfetmek ve gittikçe zorlaşıyor gibi hissetmek istiyorsanız, iyi bir tasarım deseni benimsemeyi düşünebilirsiniz: Flux .

Bu, uygulamanın genel durumunu nasıl depolayıp değiştireceğinizi ve bileşenleri oluşturmak için bu durumu nasıl kullandığınızı tanımlayan bir kurallar topluluğudur.

Birçok Flux uygulaması var ve Facebook'un resmi uygulaması bunlardan biri. Her ne kadar çoğu kazan plakası içeren kod olarak kabul edilir, ancak çoğu şey açık olduğu için anlaşılması daha kolaydır.

Diğer alternatiflerin bazıları çuvallatmak fluxxor fluxible ve redux .


0

Bir zamanlar şu anda bulunduğunuz yerdeydim, acemi olarak bazen bunu yapmanın nasıl tepki vereceği konusunda kendinizi yersiz hissedersiniz. Şimdi de aynı şekilde düşünmeye çalışacağım.

Devletler iletişimin temel taşıdır

Genellikle, bu bileşendeki durumları sizin durumunuzda değiştirmenin yolu üç bileşene işaret etmektir.

<List />: Muhtemelen bir filtreye bağlı olarak bir öğe listesi görüntüler <Filters />: Verilerinizi değiştirecek filtre seçenekleri. <TopBar />: Seçenekler listesi.

Tüm bu etkileşimi düzenlemek için, daha yüksek bir bileşene ihtiyacınız olacak.

<div>
  <List items={this.state.filteredItems}/>
  <Filter filter={this.state.filter} setFilter={setFilter}/>
</div>

Bu nedenle setFilterçağrıldığında filterItem'i etkiler ve her iki bileşeni de yeniden oluşturur. Bu tamamen açık değilse, size tek bir dosyayı kontrol edebilirsiniz onay kutusu ile bir örnek yaptı:

import React, {Component} from 'react';
import {render} from 'react-dom';

const Person  = ({person, setForDelete}) => (
          <div>
            <input type="checkbox" name="person" checked={person.checked} onChange={setForDelete.bind(this, person)} />
            {person.name}
          </div>
);


class PeopleList extends Component {

  render() {

    return(
      <div>
       {this.props.people.map((person, i) => {
         return <Person key={i} person={person} setForDelete={this.props.setForDelete} />;
       })}
       <div onClick={this.props.deleteRecords}>Delete Selected Records</div>
     </div>
    );
  }

} // end class

class App extends React.Component {

  constructor(props) {
    super(props)
    this.state = {people:[{id:1, name:'Cesar', checked:false},{id:2, name:'Jose', checked:false},{id:3, name:'Marbel', checked:false}]}
  }

  deleteRecords() {
    const people = this.state.people.filter(p => !p.checked);

    this.setState({people});
 }

  setForDelete(person) {
    const checked = !person.checked;
    const people = this.state.people.map((p)=>{
      if(p.id === person.id)
        return {name:person.name, checked};
      return p;
    });

    this.setState({people});
  }

  render () {

    return <PeopleList people={this.state.people} deleteRecords={this.deleteRecords.bind(this)} setForDelete={this.setForDelete.bind(this)}/>;
  }
}

render(<App/>, document.getElementById('app'));

0

Aşağıdaki kod, iki kardeş arasındaki iletişimi kurmama yardımcı oluyor. Kurulum, render () ve componentDidMount () çağrıları sırasında üst öğelerinde yapılır. Https://reactjs.org/docs/refs-and-the-dom.html dayanmaktadır Umarım yardımcı olur.

class App extends React.Component<IAppProps, IAppState> {
    private _navigationPanel: NavigationPanel;
    private _mapPanel: MapPanel;

    constructor() {
        super();
        this.state = {};
    }

    // `componentDidMount()` is called by ReactJS after `render()`
    componentDidMount() {
        // Pass _mapPanel to _navigationPanel
        // It will allow _navigationPanel to call _mapPanel directly
        this._navigationPanel.setMapPanel(this._mapPanel);
    }

    render() {
        return (
            <div id="appDiv" style={divStyle}>
                // `ref=` helps to get reference to a child during rendering
                <NavigationPanel ref={(child) => { this._navigationPanel = child; }} />
                <MapPanel ref={(child) => { this._mapPanel = child; }} />
            </div>
        );
    }
}

Bu TypeScript, muhtemelen cevabınızda belirtilmelidir. Gerçi iyi bir kavram.
serraosays

0

İşin garibi kimse bahsetmedi mobx. Fikir benzer redux. Birden fazla bileşenin abone olduğu bir veri parçam varsa, bu verileri birden çok bileşeni çalıştırmak için kullanabilirim.

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.