ReactJS Dua komponen berkomunikasi

321

Saya baru saja mulai dengan ReactJS dan saya sedikit terjebak pada masalah yang saya miliki.

Aplikasi saya pada dasarnya adalah daftar dengan filter dan tombol untuk mengubah tata letak. Saat ini saya menggunakan tiga komponen: <list />, < Filters />dan <TopBar />, sekarang jelas ketika saya mengubah pengaturan di < Filters />Saya ingin memicu beberapa metode dalam <list />untuk memperbarui pandangan saya.

Bagaimana saya bisa membuat 3 komponen berinteraksi satu sama lain, atau apakah saya memerlukan semacam model data global di mana saya bisa membuat perubahan?

woutr_be
sumber
Apakah ketiga komponen saudara kandung atau satu di antara yang lain?
pgreen2
Mereka semua tiga komponen, saya sudah mengatur ulang aplikasi saya sehingga mereka sekarang memiliki orang tua yang sama yang dapat memberikan mereka data.
woutr_be
4
Di sinilah Anda bisa menggunakan pola fluks atau pubsub. Berdasarkan dokumen dalam dokumen reaksi, mereka memberikan kalimat yang agak ambigu: "Untuk komunikasi antara dua komponen yang tidak memiliki hubungan orangtua-anak, Anda dapat mengatur sistem acara global Anda sendiri." facebook.github.io/react/tips/…
BingeBoy
@BingeBoy benar Flux adalah cara yang bagus untuk menulis aplikasi reactjs, yang dapat menangani masalah aliran data, berbagi data dengan banyak komponen.
Ankit Patial
4
Jika Anda tidak ingin masuk ke dalam Flux atau Redux, ini adalah artikel yang luar biasa tentang topik ini andrewhfarmer.com/component-communication
garajo

Jawaban:

318

Pendekatan terbaik akan tergantung pada bagaimana Anda berencana untuk mengatur komponen-komponen itu. Beberapa contoh skenario yang muncul saat ini:

  1. <Filters /> adalah komponen anak dari <List />
  2. Keduanya <Filters />dan <List />merupakan anak-anak dari komponen orang tua
  3. <Filters />dan <List />hidup dalam komponen root yang terpisah sepenuhnya.

Mungkin ada skenario lain yang tidak saya pikirkan. Jika milik Anda tidak sesuai dengan ini, maka beri tahu saya. Berikut adalah beberapa contoh kasar tentang bagaimana saya menangani dua skenario pertama:

Skenario 1

Anda bisa meneruskan pawang dari <List />ke <Filters />, yang kemudian bisa dipanggil pada onChangeacara untuk menyaring daftar dengan nilai saat ini.

JSFiddle untuk # 1 →

/** @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);

Skenario # 2

Mirip dengan skenario # 1, tetapi komponen induk akan menjadi yang meneruskan fungsi pengendali <Filters />, dan akan meneruskan daftar yang difilter ke <List />. Saya suka metode ini lebih baik karena memisahkan <List />dari <Filters />.

JSFiddle untuk # 2 →

/** @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);

Skenario # 3

Ketika komponen tidak dapat berkomunikasi antara hubungan orangtua-anak, dokumentasi merekomendasikan pembuatan sistem acara global .

Michael LaCroix
sumber
6
Yang menyenangkan dengan # 2 adalah bahwa mereka hanya bergantung pada orangtua yang meneruskan prop ke setiap komponen: fungsi updateFilteruntuk <Filters />dan array itemsuntuk <List />. Anda bisa menggunakan komponen anak-anak itu pada orang tua lain dengan perilaku berbeda, baik bersama atau sendiri. Misalnya, jika Anda ingin menampilkan daftar dinamis tetapi tidak perlu pemfilteran.
Michael LaCroix
2
@woutr_be Tidak yakin apakah itu akan sesuai dengan kebutuhan Anda tetapi ketika dalam situasi yang sama beberapa waktu lalu, kami menggunakan dua fungsi berikut untuk mengurutkan komunikasi antara komponen anak dan orang tua: - listenTo: function (eventName, eventCallback) {$ ( window.document) .bind (eventName, eventCallback);} triggerEvent: function (eventName, params) {$ .event.trigger (eventName, params);} Semoga bermanfaat! (maaf tidak bisa memformatnya dengan lebih baik)
5122014009
29
Untuk skenario 3, apakah ada pendekatan yang disarankan? Adakah dokumen atau contoh tentang ini dengan membuat acara sintetis khusus? Saya belum menemukan apa pun di dokumen utama.
pwray
1
Skenario # 2 membuat banyak akal ... sampai Anda harus membahayakan desain (jika hanya, Layout) - kemudian Anda menyadari perlunya untuk EventHub / PubSub.
Cody
4
Tautan skenario # 3 sudah mati dan mengarahkan ke halaman React docs yang tidak terkait sekarang.
beporter
170

Ada beberapa cara untuk membuat komponen berkomunikasi. Beberapa dapat cocok dengan usecase Anda. Berikut adalah daftar yang menurut saya berguna untuk diketahui.

Reaksi

Komunikasi langsung orang tua / anak

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}/>
    )
  }
}

Di sini komponen anak akan memanggil panggilan balik yang diberikan oleh orang tua dengan nilai, dan orang tua akan bisa mendapatkan nilai yang diberikan oleh anak-anak pada orang tua.

Jika Anda membangun fitur / halaman aplikasi Anda, lebih baik memiliki orang tua tunggal yang mengelola panggilan balik / negara bagian (juga disebut containeratau smart component), dan semua anak tidak memiliki kewarganegaraan, hanya melaporkan hal-hal kepada orang tua. Dengan cara ini Anda dapat dengan mudah "membagikan" keadaan orang tua kepada setiap anak yang membutuhkannya.


Konteks

Bereaksi Konteks memungkinkan untuk mempertahankan status pada akar hierarki komponen Anda, dan dapat menyuntikkan status ini dengan mudah ke komponen bersarang yang sangat dalam, tanpa harus repot-repot menyerahkan props ke setiap komponen perantara.

Sampai sekarang, konteks adalah fitur eksperimental, tetapi API baru tersedia di Bereaksi 16.3.

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

Konsumen menggunakan pola fungsi penyangga / anak-anak

Lihat posting blog ini untuk lebih jelasnya.

Sebelum Bereaksi 16.3, saya akan merekomendasikan menggunakan siaran reaksi yang menawarkan API yang sangat mirip, dan menggunakan API konteks sebelumnya.


Portal

Gunakan portal ketika Anda ingin menjaga 2 komponen tetap berdekatan agar mereka berkomunikasi dengan fungsi-fungsi sederhana, seperti pada orang tua / anak normal, tetapi Anda tidak ingin kedua komponen ini memiliki hubungan orang tua / anak di DOM, karena kendala visual / CSS yang disiratkannya (seperti z-index, opacity ...).

Dalam hal ini Anda dapat menggunakan "portal". Ada berbagai perpustakaan reaksi yang menggunakan portal , biasanya digunakan untuk modals , popup, tooltips ...

Pertimbangkan yang berikut ini:

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

Dapat menghasilkan DOM berikut saat dirender di dalam reactAppContainer:

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

Lebih detail di sini


Slot

Anda menentukan slot di suatu tempat, dan kemudian Anda mengisi slot dari tempat lain dari render tree Anda.

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>

Ini agak mirip dengan portal kecuali konten yang diisi akan diberikan dalam slot yang Anda tentukan, sementara portal umumnya membuat simpul dom baru (sering kali anak-anak document.body)

Periksa pustaka slot-isi reaksi


Bus acara

Sebagaimana dinyatakan dalam dokumentasi Bereaksi :

Untuk komunikasi antara dua komponen yang tidak memiliki hubungan orangtua-anak, Anda dapat mengatur sistem acara global Anda sendiri. Berlangganan acara di componentDidMount (), berhenti berlangganan di componentWillUnmount (), dan panggil setState () saat Anda menerima suatu peristiwa.

Ada banyak hal yang dapat Anda gunakan untuk mengatur bus acara. Anda bisa membuat susunan pendengar, dan pada acara penerbitan, semua pendengar akan menerima acara tersebut. Atau Anda dapat menggunakan sesuatu seperti EventEmitter atau PostalJs


Aliran

Flux pada dasarnya adalah sebuah bus acara, kecuali penerima acara adalah toko. Ini mirip dengan sistem bus peristiwa dasar kecuali negara dikelola di luar Bereaksi

Implementasi Flux asli terlihat seperti upaya untuk melakukan Event-sourcing dengan cara yang hacky.

Redux bagi saya implementasi Flux yang paling dekat dari event-sourcing, manfaat banyak keuntungan event-sourcing seperti kemampuan untuk melakukan perjalanan waktu. Itu tidak benar-benar terkait dengan Bereaksi dan juga dapat digunakan dengan perpustakaan tampilan fungsional lainnya.

Tutorial video Redux dari Egghead sangat bagus dan menjelaskan cara kerjanya secara internal (sangat sederhana).


Kursor

Kursor berasal dari ClojureScript / Om dan banyak digunakan dalam proyek Bereaksi. Mereka mengizinkan untuk mengelola keadaan di luar Bereaksi, dan membiarkan beberapa komponen memiliki akses baca / tulis ke bagian yang sama dari negara tersebut, tanpa perlu tahu apa-apa tentang pohon komponen.

Banyak implementasi yang ada, termasuk ImmutableJS , React-cursors dan Omniscient

Sunting 2016 : tampaknya orang setuju kursor berfungsi dengan baik untuk aplikasi yang lebih kecil tetapi tidak dapat diukur dengan baik pada aplikasi kompleks. Om Next tidak memiliki kursor lagi (sementara itu Om yang memperkenalkan konsep awalnya)


Arsitektur Elm

The arsitektur Elm adalah arsitektur yang diusulkan untuk digunakan oleh bahasa Elm . Bahkan jika Elm bukan ReactJS, arsitektur Elm dapat dilakukan di React juga.

Dan Abramov, penulis Redux, melakukan implementasi arsitektur Elm menggunakan React.

Baik Redux dan Elm benar-benar hebat dan cenderung memberdayakan konsep-konsep sumber acara di frontend, keduanya memungkinkan debugging perjalanan-waktu, undo / redo, replay ...

Perbedaan utama antara Redux dan Elm adalah bahwa Elm cenderung jauh lebih ketat tentang manajemen negara. Di Elm, Anda tidak dapat memiliki status komponen lokal atau memasang / melepas kait dan semua perubahan DOM harus dipicu oleh perubahan keadaan global. Arsitektur Elm mengusulkan pendekatan terukur yang memungkinkan untuk menangani SEMUA keadaan di dalam objek tunggal yang tidak dapat diubah, sementara Redux mengusulkan pendekatan yang mengundang Anda untuk menangani PALING keadaan di dalam satu objek tak berubah.

Meskipun model konseptual Elm sangat elegan dan arsitektur memungkinkan untuk mengukur dengan baik pada aplikasi besar, dalam praktiknya dapat menjadi sulit atau melibatkan lebih banyak pelat untuk mencapai tugas-tugas sederhana seperti memberikan fokus pada input setelah pemasangan, atau mengintegrasikan dengan perpustakaan yang ada dengan antarmuka imperatif (yaitu plugin JQuery). Masalah terkait .

Juga, arsitektur Elm melibatkan lebih banyak kode boilerplate. Bukan verbose atau rumit untuk menulis tapi saya pikir arsitektur Elm lebih cocok untuk bahasa yang diketik secara statis.


FRP

Perpustakaan seperti RxJS, BaconJS atau Kefir dapat digunakan untuk menghasilkan aliran FRP untuk menangani komunikasi antar komponen.

Anda dapat mencoba misalnya Rx-React

Saya pikir menggunakan lib ini sangat mirip dengan menggunakan apa yang ditawarkan bahasa ELM dengan sinyal .

Kerangka kerja CycleJS tidak menggunakan ReactJS tetapi menggunakan vdom . Ini berbagi banyak kesamaan dengan arsitektur Elm (tetapi lebih mudah digunakan dalam kehidupan nyata karena memungkinkan kait vdom) dan menggunakan RxJ secara luas alih-alih fungsi, dan dapat menjadi sumber inspirasi yang baik jika Anda ingin menggunakan FRP dengan Reaksi. Video CycleJs Egghead bagus untuk memahami cara kerjanya.


CSP

CSP (Communicating Sequential Processes) saat ini populer (kebanyakan karena Go / goroutine dan core.async / ClojureScript) tetapi Anda dapat menggunakannya juga dalam javascript dengan JS-CSP .

James Long telah melakukan video yang menjelaskan bagaimana itu dapat digunakan dengan React.

Sagas

Saga adalah konsep backend yang berasal dari dunia DDD / EventSourcing / CQRS, juga disebut "manajer proses". Itu sedang dipopulerkan oleh proyek redux-saga , sebagian besar sebagai pengganti redux-thunk untuk menangani efek samping (yaitu panggilan API dll). Sebagian besar orang saat ini berpikir itu hanya layanan untuk efek samping tetapi sebenarnya lebih lanjut tentang komponen decoupling.

Ini lebih merupakan pujian untuk arsitektur Flux (atau Redux) daripada sistem komunikasi yang sama sekali baru, karena kisah ini memancarkan tindakan Flux pada akhirnya. Idenya adalah bahwa jika Anda memiliki widget1 dan widget2, dan Anda ingin mereka dipisahkan, Anda tidak dapat memecat penargetan tindakan widget2 dari widget1. Jadi, Anda membuat widget1 hanya melakukan aksi yang menargetkan dirinya sendiri, dan kisahnya adalah "proses latar belakang" yang mendengarkan tindakan widget1, dan dapat mengirimkan tindakan yang menargetkan widget2. Saga adalah titik penghubung antara 2 widget tetapi widget tetap dipisahkan.

Jika Anda tertarik lihat jawaban saya di sini


Kesimpulan

Jika Anda ingin melihat contoh aplikasi kecil yang sama menggunakan gaya berbeda ini, periksa cabang repositori ini .

Saya tidak tahu apa pilihan terbaik dalam jangka panjang, tetapi saya sangat suka bagaimana Flux terlihat seperti sumber acara.

Jika Anda tidak tahu konsep acara-sumber, lihatlah blog sangat pedagogik ini: Beralih di dalam basis data dengan apache Samza , itu adalah suatu keharusan-baca untuk memahami mengapa Flux bagus (tapi ini bisa berlaku untuk FRP serta )

Saya pikir komunitas setuju bahwa implementasi Flux yang paling menjanjikan adalah Redux , yang akan semakin memungkinkan pengalaman pengembang yang sangat produktif berkat pemuatan ulang yang panas. Video live yang mengesankan ala Bret Victor's Inventing on Principle video dimungkinkan!

Sebastien Lorber
sumber
2
Jawaban sempurna Seb!
Ali Gajani
7

OK, ada beberapa cara untuk melakukannya, tetapi saya secara eksklusif ingin fokus menggunakan toko menggunakan Redux yang membuat hidup Anda lebih mudah untuk situasi ini daripada memberikan Anda solusi cepat hanya untuk kasus ini, menggunakan pure React akan berakhir berantakan di aplikasi besar nyata dan komunikasi antara Komponen menjadi semakin sulit saat aplikasi tumbuh ...

Jadi apa yang Redux lakukan untuk Anda?

Redux seperti penyimpanan lokal di aplikasi Anda yang dapat digunakan kapan pun Anda membutuhkan data untuk digunakan di berbagai tempat di aplikasi Anda ...

Pada dasarnya, ide Redux berasal dari fluks awalnya, tetapi dengan beberapa perubahan mendasar termasuk konsep memiliki satu sumber kebenaran dengan menciptakan hanya satu toko ...

Lihatlah grafik di bawah ini untuk melihat beberapa perbedaan antara Flux dan Redux ...

Redux dan Flux

Pertimbangkan penerapan Redux di aplikasi Anda sejak awal jika aplikasi Anda membutuhkan komunikasi antar Komponen ...

Juga membaca kata-kata ini dari Dokumentasi Redux bisa membantu untuk memulai dengan:

Karena persyaratan untuk aplikasi satu halaman JavaScript menjadi semakin rumit, kode kami harus mengelola lebih banyak status daripada sebelumnya . Keadaan ini dapat mencakup respons server dan data yang di-cache, serta data yang dibuat secara lokal yang belum ada di server. Keadaan UI juga semakin kompleks, karena kami perlu mengelola rute aktif, tab terpilih, pemintal, kontrol pagination, dan sebagainya.

Mengelola keadaan yang terus berubah ini sulit. Jika model dapat memperbarui model lain, maka tampilan dapat memperbarui model, yang memperbarui model lain, dan ini, pada gilirannya, dapat menyebabkan tampilan lain diperbarui. Pada titik tertentu, Anda tidak lagi mengerti apa yang terjadi di aplikasi Anda karena Anda kehilangan kendali atas kapan, mengapa, dan bagaimana kondisinya. Ketika suatu sistem buram dan non-deterministik, sulit untuk mereproduksi bug atau menambahkan fitur baru.

Seolah ini tidak cukup buruk, pertimbangkan persyaratan baru yang umum dalam pengembangan produk front-end. Sebagai pengembang, kami diharapkan untuk menangani pembaruan yang optimis, rendering sisi server, pengambilan data sebelum melakukan transisi rute, dan sebagainya. Kami mendapati diri kami berusaha mengelola kompleksitas yang belum pernah kami hadapi sebelumnya, dan kami mau tak mau mengajukan pertanyaan: apakah ini saatnya untuk menyerah? Jawabannya adalah tidak.

Kompleksitas ini sulit untuk ditangani karena kami sedang mencampur dua konsep yang sangat sulit untuk dipikirkan oleh manusia: mutasi dan asinkronisitas. Saya menyebutnya Mentos dan Coke. Keduanya bisa menjadi hebat dalam pemisahan, tetapi bersama-sama mereka membuat kekacauan. Perpustakaan seperti React berupaya untuk memecahkan masalah ini di lapisan tampilan dengan menghapus kedua asynchrony dan manipulasi DOM langsung. Namun, mengelola keadaan data Anda diserahkan kepada Anda. Di sinilah Redux masuk.

Mengikuti langkah-langkah Flux, CQRS, dan Pengadaan Acara , Redux berupaya membuat mutasi negara dapat diprediksi dengan menerapkan batasan tertentu tentang bagaimana dan kapan pembaruan dapat terjadi . Pembatasan ini tercermin dalam tiga prinsip Redux.

Alireza
sumber
Bagaimana redux dapat membantu? jika saya memiliki modal untuk datepicker(sebagai komponen) dan komponen itu dapat dimuat dari beberapa komponen yang hidup di halaman yang sama, lalu bagaimana datepickerkomponen itu tahu Tindakan yang harus dikirim ke redux? Ini adalah inti dari masalah, menghubungkan suatu tindakan dalam satu komponen dengan komponen lainnya dan BUKAN komponen lainnya . (Mempertimbangkan datepickersendiri itu adalah komponen yang dalam dan dalam di dalam komponen modal itu sendiri)
vsync
@vsync tidak berpikir reudx sebagai nilai statis tunggal, redux sebenarnya dapat memiliki objek, array ... sehingga Anda dapat menyimpannya sebagai objek atau array atau apa pun di toko Anda, itu bisa berupa mapdispatchtoprops dan masing-masing disimpan dalam array objek seperti: [{name: "picker1", nilai: "01/01/1970"}, {name: "picker2", nilai: "01/01/1980"}] dan kemudian gunakan mapstatetoprops di induk dan kirimkan ke masing-masing komponen atau di mana pun Anda inginkan, tidak yakin apakah itu menjawab pertanyaan Anda, tetapi tanpa melihat kode ... jika mereka berada dalam kelompok yang terpisah, Anda dapat mengajukan keberatan dengan rincian lebih lanjut juga ... tetapi semua terserah bagaimana Anda ingin mengelompokkan mereka ..
Alireza
Pertanyaannya bukan tentang reduxdan apa yang bisa Anda simpan, tetapi BAGAIMANA untuk meneruskan tindakan itu jauh ke dalam apa yang perlu memicu itu. Bagaimana komponen yang dalam tahu apa yang perlu dipicu secara PERSIS ? karena dalam contoh saya memberikan komponen umum yang harus memicu ke peredam tertentu, tergantung pada skenario, sehingga bisa menjadi peredam yang berbeda karena modal datepicker dapat digunakan untuk komponen apa pun.
vsync
5

Ini cara saya menangani ini.
Katakanlah Anda memiliki <seleksi> untuk Bulan dan <seleksi> untuk Hari . Jumlah hari tergantung pada bulan yang dipilih.

Kedua daftar dimiliki oleh objek ketiga, panel kiri. Keduanya <select> juga anak-anak dari leftPanel <div>
Ini adalah game dengan callback dan penangan dalam komponen LeftPanel .

Untuk mengujinya, cukup salin kode ke dua file yang terpisah dan jalankan index.html . Kemudian pilih satu bulan dan lihat bagaimana jumlah hari berubah.

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

Dan HTML untuk menjalankan index.html komponen panel kiri

<!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>
Skulas
sumber
jika Anda dapat menghapus 80% kode contoh dan tetap mempertahankan poin Anda, itu akan lebih baik. menunjukkan CSS dalam konteks utas ini tidak relevan
vsync
3

Saya melihat bahwa pertanyaannya sudah dijawab, tetapi jika Anda ingin mempelajari lebih lanjut, ada total 3 kasus komunikasi antar komponen :

  • Kasus 1: Komunikasi orang tua ke anak
  • Kasus 2: Komunikasi Anak ke Orangtua
  • Kasus 3: Komunikasi komponen yang tidak terkait (komponen apa pun dengan komponen apa pun)
Kaloyan Kosev
sumber
1

Memperluas jawaban dari @MichaelLaCroix ketika sebuah skenario adalah bahwa komponen tidak dapat berkomunikasi antara segala jenis hubungan orangtua-anak, dokumentasi merekomendasikan untuk menyiapkan sistem acara global.

Dalam kasus <Filters />dan <TopBar />tidak memiliki hubungan di atas, emitor global sederhana dapat digunakan seperti ini:

componentDidMount - Berlangganan acara

componentWillUnmount - Berhenti berlangganan dari acara

React.js dan kode EventSystem

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() {

    }
}
tsuz
sumber
0

Ada kemungkinan seperti itu bahkan jika mereka bukan orang tua - hubungan anak - dan itu fluks. Ada implementasi (untuk saya pribadi) yang cukup bagus untuk yang disebut Alt.JS (dengan Alt-Container).

Misalnya, Anda dapat memiliki Bilah Samping yang bergantung pada apa yang diatur dalam Rincian komponen. Sidebar Komponen terhubung dengan SidebarActions dan SidebarStore, sedangkan Detail adalah DetailsActions dan DetailsStore.

Anda bisa menggunakan AltContainer lalu seperti itu

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

{this.props.content}

Yang akan menyimpan toko (baik saya bisa menggunakan "toko" bukan prop "toko"). Sekarang, {this.props.content} BISA MENJADI Detail tergantung pada rute. Mari kita katakan / Detail mengarahkan kita ke tampilan itu. Detail akan memiliki misalnya kotak centang yang akan mengubah elemen Sidebar dari X ke Y jika akan diperiksa.

Secara teknis tidak ada hubungan di antara mereka dan itu akan sulit dilakukan tanpa fluks. TETAPI DENGAN ITU agak mudah.

Sekarang mari kita ke DetailsActions. Kami akan membuat di sana

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

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

dan DetailsStore

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

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

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

Dan sekarang, ini adalah tempat di mana sihir dimulai.

Seperti yang Anda lihat ada bindListener ke SidebarActions.ComponentStatusChanged yang akan digunakan JIKA setSiteComponent akan digunakan.

sekarang di SidebarActions

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

Kami punya hal seperti itu. Ini akan mengirimkan objek itu saat dipanggil. Dan itu akan dipanggil jika setSiteComponent di store akan digunakan (yang dapat Anda gunakan dalam komponen misalnya selama onChange on Button atau apa pun)

Sekarang di SidebarStore akan kita miliki

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

Sekarang di sini Anda dapat melihat, bahwa ia akan menunggu DetailsStore. Apa artinya? lebih atau kurang itu berarti bahwa metode ini perlu menunggu pembaruan DetailsStoreto sebelum dapat memperbarui sendiri.

tl; dr One Store mendengarkan metode di toko, dan akan memicu tindakan dari tindakan komponen, yang akan memperbarui toko sendiri.

Saya harap ini bisa membantu Anda.

Shiroo
sumber
0

Jika Anda ingin menjelajahi opsi komunikasi antar komponen dan merasa semakin sulit, maka Anda dapat mempertimbangkan untuk mengadopsi pola desain yang baik: Flux .

Ini hanyalah kumpulan aturan yang menentukan bagaimana Anda menyimpan dan mengubah status aplikasi yang luas, dan menggunakan status tersebut untuk membuat komponen.

Ada banyak implementasi Flux, dan implementasi resmi Facebook adalah salah satunya. Meskipun dianggap salah satu yang berisi sebagian besar kode boilerplate, tetapi lebih mudah dipahami karena sebagian besar hal-hal itu eksplisit.

Beberapa alternatif lainnya adalah membingungkan fluxxor fluxible dan redux .

Kemal Dağ
sumber
0

Saya pernah berada di tempat Anda berada sekarang, sebagai seorang pemula Anda kadang merasa tidak pada tempatnya tentang bagaimana reaksinya melakukan hal ini. Saya akan mencoba untuk menangani cara yang sama yang saya pikirkan sekarang.

Negara adalah landasan komunikasi

Biasanya apa yang turun adalah cara Anda mengubah keadaan dalam komponen ini dalam kasus Anda, Anda menunjukkan tiga komponen.

<List />: Yang mungkin akan menampilkan daftar item tergantung pada filter <Filters />: Opsi filter yang akan mengubah data Anda. <TopBar />: Daftar opsi.

Untuk mengatur semua interaksi ini, Anda akan memerlukan komponen yang lebih tinggi, sebut saja Aplikasi, yang akan meneruskan tindakan dan data ke masing-masing komponen ini sehingga misalnya dapat terlihat seperti ini

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

Jadi ketika setFilterdipanggil itu akan mempengaruhi filteredItem dan merender ulang kedua komponen ;. Dalam hal ini tidak sepenuhnya jelas saya membuat Anda contoh dengan kotak centang yang dapat Anda periksa dalam satu file:

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'));
cabolanoz
sumber
0

Kode berikut membantu saya mengatur komunikasi antara dua saudara kandung. Penyiapan dilakukan di induknya selama panggilan render () dan componentDidMount (). Ini didasarkan pada https://reactjs.org/docs/refs-and-the-dom.html Semoga ini bisa membantu.

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>
        );
    }
}
Sergei Zinovyev
sumber
Ini adalah TypeScript, mungkin harus disebutkan dalam jawaban Anda. Konsep yang bagus.
serraosays
0

Anehnya tidak ada yang disebutkan mobx. Idenya mirip dengan redux. Jika saya memiliki sepotong data yang berlangganan beberapa komponen, maka saya dapat menggunakan data ini untuk mendorong banyak komponen.

windmaomao
sumber