Sunting : lihat contoh akhir untuk contoh terbaru ES6.
Jawaban ini hanya menangani kasus hubungan orangtua-anak langsung. Ketika orang tua dan anak berpotensi memiliki banyak perantara, periksa jawaban ini .
Solusi lain tidak ada gunanya
Sementara mereka masih bekerja dengan baik, jawaban lain kehilangan sesuatu yang sangat penting.
Apakah tidak ada cara sederhana untuk meneruskan alat peraga anak ke orang tuanya menggunakan acara, di React.js?
Orang tua sudah memiliki properti anak itu! : jika anak memiliki alat peraga, maka itu karena orang tuanya menyediakan alat peraga itu kepada anak itu! Mengapa Anda ingin anak itu memberikan kembali prop kepada orang tua, sementara orangtua jelas sudah memiliki prop itu?
Implementasi yang lebih baik
Anak : itu benar-benar tidak harus lebih rumit dari itu.
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
Orang tua dengan anak tunggal : menggunakan nilai yang diberikan kepada anak
var Parent = React.createClass({
getInitialState: function() {
return {childText: "Click me! (parent prop)"};
},
render: function () {
return (
<Child onClick={this.handleChildClick} text={this.state.childText}/>
);
},
handleChildClick: function(event) {
// You can access the prop you pass to the children
// because you already have it!
// Here you have it in state but it could also be
// in props, coming from another parent.
alert("The Child button text is: " + this.state.childText);
// You can also access the target of the click here
// if you want to do some magic stuff
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
Orangtua dengan daftar anak : Anda masih memiliki semua yang Anda butuhkan pada orang tua dan tidak perlu membuat anak lebih rumit.
var Parent = React.createClass({
getInitialState: function() {
return {childrenData: [
{childText: "Click me 1!", childNumber: 1},
{childText: "Click me 2!", childNumber: 2}
]};
},
render: function () {
var children = this.state.childrenData.map(function(childData,childIndex) {
return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;
}.bind(this));
return <div>{children}</div>;
},
handleChildClick: function(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
Dimungkinkan juga untuk digunakan this.handleChildClick.bind(null,childIndex)
dan kemudian digunakanthis.state.childrenData[childIndex]
Catatan kami mengikat dengan null
konteks karena jika tidak Bereaksi mengeluarkan peringatan yang terkait dengan sistem autobinding -nya . Menggunakan null berarti Anda tidak ingin mengubah konteks fungsi. Lihat juga .
Tentang enkapsulasi dan penggabungan dalam jawaban lain
Bagi saya ini adalah ide buruk dalam hal kopling dan enkapsulasi:
var Parent = React.createClass({
handleClick: function(childComponent) {
// using childComponent.props
// using childComponent.refs.button
// or anything else using childComponent
},
render: function() {
<Child onClick={this.handleClick} />
}
});
Menggunakan alat peraga : Seperti yang saya jelaskan di atas, Anda sudah memiliki alat peraga di induk sehingga tidak berguna untuk melewatkan seluruh komponen anak untuk mengakses alat peraga.
Menggunakan referensi : Anda sudah memiliki target klik dalam acara tersebut, dan dalam kebanyakan kasus ini sudah cukup. Selain itu, Anda bisa menggunakan referensi langsung pada anak:
<Child ref="theChild" .../>
Dan akses simpul DOM di induk dengan
React.findDOMNode(this.refs.theChild)
Untuk kasus yang lebih lanjut di mana Anda ingin mengakses beberapa referensi anak di orang tua, anak tersebut dapat melewati semua node dom langsung di callback.
Komponen memiliki antarmuka (alat peraga) dan orang tua tidak boleh menganggap apa pun tentang pekerjaan batin anak, termasuk struktur DOM bagian dalamnya atau yang dideklarasikan oleh simpul DOM. Orang tua yang menggunakan referensi anak berarti Anda memasangkan kedua komponen tersebut dengan erat.
Untuk mengilustrasikan masalah ini, saya akan mengambil kutipan ini tentang Shadow DOM , yang digunakan di dalam browser untuk membuat hal-hal seperti slider, scrollbars, pemutar video ...:
Mereka menciptakan batas antara apa yang Anda, pengembang Web dapat capai dan apa yang dianggap detail implementasi, sehingga tidak dapat diakses oleh Anda. Namun browser, dapat melintasi batas ini sesuka hati. Dengan batas ini di tempat, mereka dapat membangun semua elemen HTML menggunakan teknologi Web lama yang sama, dari div dan bentang seperti yang Anda inginkan.
Masalahnya adalah bahwa jika Anda membiarkan detail implementasi anak bocor ke orang tua, Anda membuatnya sangat sulit untuk refactor anak tanpa mempengaruhi orang tua. Ini berarti sebagai penulis perpustakaan (atau sebagai editor peramban dengan Shadow DOM) ini sangat berbahaya karena Anda membiarkan klien mengakses terlalu banyak, sehingga sangat sulit untuk memutakhirkan kode tanpa melanggar retrokompatibilitas.
Jika Chrome telah menerapkan bilah gulirnya sehingga klien dapat mengakses node dom bagian dalam bilah gulir itu, ini berarti bahwa klien mungkin memiliki kemungkinan untuk hanya menghancurkan bilah gulir itu, dan bahwa aplikasi akan lebih mudah pecah ketika Chrome melakukan pembaruan otomatis setelah refactoring. scrollbar ... Alih-alih, mereka hanya memberikan akses ke beberapa hal yang aman seperti menyesuaikan beberapa bagian scrollbar dengan CSS.
Tentang menggunakan hal lain
Melewati seluruh komponen dalam panggilan balik itu berbahaya dan dapat menyebabkan pengembang pemula melakukan hal-hal yang sangat aneh seperti menelepon childComponent.setState(...)
atau childComponent.forceUpdate()
, atau menugaskannya variabel baru, di dalam induk, membuat seluruh aplikasi jauh lebih sulit untuk dipikirkan.
Edit: contoh ES6
Seperti banyak orang sekarang menggunakan ES6, berikut adalah contoh yang sama untuk sintaks ES6
Anak itu bisa sangat sederhana:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
Orang tua dapat berupa kelas (dan akhirnya dapat mengelola negara itu sendiri, tapi saya meneruskannya sebagai alat peraga di sini:
class Parent1 extends React.Component {
handleChildClick(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
render() {
return (
<div>
{this.props.childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => this.handleChildClick(child,e)}
/>
))}
</div>
);
}
}
Tetapi ini juga dapat disederhanakan jika tidak perlu mengatur status:
const Parent2 = ({childrenData}) => (
<div>
{childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => {
alert("The Child button data is: " + child.childText + " - " + child.childNumber);
alert("The Child HTML is: " + e.target.outerHTML);
}}
/>
))}
</div>
)
JsFiddle
PERF PERINGATAN (berlaku untuk ES5 / ES6): jika Anda menggunakan PureComponent
atau shouldComponentUpdate
, implementasi di atas tidak akan dioptimalkan secara default karena menggunakan onClick={e => doSomething()}
, atau mengikat langsung selama fase render, karena itu akan membuat fungsi baru setiap kali orangtua membuat. Jika ini adalah hambatan utama dalam aplikasi Anda, Anda dapat meneruskan data kepada anak-anak, dan memasukkannya kembali ke dalam panggilan balik "stabil" (ditetapkan pada kelas induk, dan diikat ke this
dalam konstruktor kelas) sehingga PureComponent
optimisasi dapat dimulai, atau Anda dapat menerapkan sendiri shouldComponentUpdate
dan mengabaikan panggilan balik dalam pemeriksaan perbandingan alat peraga.
Anda juga dapat menggunakan pustaka komposisi ulang , yang menyediakan komponen pesanan lebih tinggi untuk mencapai optimisasi yang disempurnakan:
// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}
// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)
// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)
Dalam hal ini Anda dapat mengoptimalkan komponen Anak dengan menggunakan:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)