Chỉnh sửa : xem các ví dụ kết thúc cho các ví dụ cập nhật ES6.
Câu trả lời này chỉ đơn giản là xử lý các trường hợp quan hệ cha mẹ trực tiếp. Khi cha mẹ và con cái có khả năng có nhiều trung gian, hãy kiểm tra câu trả lời này .
Các giải pháp khác đang thiếu điểm
Trong khi chúng vẫn hoạt động tốt, các câu trả lời khác đang thiếu một cái gì đó rất quan trọng.
Có cách nào đơn giản để truyền đạo cụ của một đứa trẻ cho cha mẹ của nó bằng các sự kiện, trong React.js không?
Cha mẹ đã có chỗ dựa cho con! : nếu đứa trẻ có chỗ dựa, thì đó là vì cha mẹ của nó đã cung cấp chỗ dựa đó cho đứa trẻ! Tại sao bạn muốn đứa trẻ trả lại chỗ dựa cho cha mẹ, trong khi cha mẹ rõ ràng đã có chỗ dựa đó?
Thực hiện tốt hơn
Đứa trẻ : nó thực sự không phải phức tạp hơn thế.
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
Cha mẹ có con độc thân : sử dụng giá trị nó truyền cho con
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
Cha mẹ có danh sách con cái : bạn vẫn có mọi thứ bạn cần cho cha mẹ và không cần làm cho con trở nên phức tạp hơn.
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
Cũng có thể sử dụng this.handleChildClick.bind(null,childIndex)
và sau đó sử dụngthis.state.childrenData[childIndex]
Lưu ý rằng chúng tôi đang ràng buộc với một null
bối cảnh vì nếu không React sẽ đưa ra cảnh báo liên quan đến hệ thống tự động gắn kết của nó . Sử dụng null có nghĩa là bạn không muốn thay đổi bối cảnh chức năng. Xem thêm .
Về đóng gói và khớp nối trong các câu trả lời khác
Đây là một ý tưởng tồi đối với việc ghép và đóng gói:
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} />
}
});
Sử dụng đạo cụ : Như tôi đã giải thích ở trên, bạn đã có đạo cụ trong cha mẹ nên việc truyền toàn bộ thành phần con để truy cập đạo cụ là vô ích.
Sử dụng refs : Bạn đã có mục tiêu nhấp chuột trong sự kiện và trong hầu hết các trường hợp, điều này là đủ. Ngoài ra, bạn có thể đã sử dụng một ref trực tiếp cho trẻ:
<Child ref="theChild" .../>
Và truy cập nút DOM trong cha mẹ với
React.findDOMNode(this.refs.theChild)
Đối với các trường hợp nâng cao hơn mà bạn muốn truy cập nhiều ref của đứa trẻ trong cha mẹ, đứa trẻ có thể chuyển tất cả các nút dom trực tiếp trong cuộc gọi lại.
Thành phần này có một giao diện (đạo cụ) và cha mẹ không nên thừa nhận bất cứ điều gì về hoạt động bên trong của đứa trẻ, bao gồm cả cấu trúc DOM bên trong của nó hoặc các nút DOM mà nó tuyên bố giới thiệu. Một phụ huynh sử dụng ref của một đứa trẻ có nghĩa là bạn kết hợp chặt chẽ 2 thành phần.
Để minh họa vấn đề, tôi sẽ lấy trích dẫn này về Shadow DOM , được sử dụng bên trong các trình duyệt để hiển thị những thứ như thanh trượt, thanh cuộn, trình phát video ...:
Họ đã tạo ra một ranh giới giữa những gì bạn, nhà phát triển Web có thể tiếp cận và những gì được coi là chi tiết triển khai, do đó không thể tiếp cận được với bạn. Tuy nhiên, trình duyệt có thể vượt qua ranh giới này tùy ý. Với ranh giới này, họ có thể xây dựng tất cả các yếu tố HTML bằng cách sử dụng cùng các công nghệ Web cũ, ngoài các div và spans giống như bạn.
Vấn đề là nếu bạn để trẻ thực hiện các chi tiết rò rỉ vào cha mẹ, bạn sẽ rất khó để cấu trúc lại trẻ mà không ảnh hưởng đến cha mẹ. Điều này có nghĩa là với tư cách là một tác giả thư viện (hoặc là một trình soạn thảo trình duyệt với Shadow DOM), điều này rất nguy hiểm vì bạn cho phép máy khách truy cập quá nhiều, khiến cho việc nâng cấp mã rất khó mà không phá vỡ tính tương thích.
Nếu Chrome đã triển khai thanh cuộn của mình cho phép khách hàng truy cập vào các nút dom bên trong của thanh cuộn đó, điều này có nghĩa là khách hàng có thể có khả năng phá vỡ thanh cuộn đó và các ứng dụng sẽ dễ dàng phá vỡ hơn khi Chrome thực hiện cập nhật tự động sau khi tái cấu trúc thanh cuộn ... Thay vào đó, họ chỉ cấp quyền truy cập vào một số thứ an toàn như tùy chỉnh một số phần của thanh cuộn bằng CSS.
Về việc sử dụng bất cứ điều gì khác
Truyền toàn bộ thành phần trong cuộc gọi lại rất nguy hiểm và có thể khiến các nhà phát triển mới làm những việc rất kỳ lạ như gọi childComponent.setState(...)
hoặc childComponent.forceUpdate()
hoặc gán cho nó các biến mới, bên trong cha mẹ, khiến cho toàn bộ ứng dụng trở nên khó khăn hơn nhiều.
Chỉnh sửa: ví dụ ES6
Như nhiều người hiện đang sử dụng ES6, đây là những ví dụ tương tự cho cú pháp ES6
Đứa trẻ có thể rất đơn giản:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
Phụ huynh có thể là một lớp (và cuối cùng nó có thể tự quản lý trạng thái, nhưng tôi chuyển nó làm đạo cụ ở đây:
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>
);
}
}
Nhưng nó cũng có thể được đơn giản hóa nếu không cần quản lý trạng thái:
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
CẢNH BÁO PERF (áp dụng cho ES5 / ES6): nếu bạn đang sử dụng PureComponent
hoặc shouldComponentUpdate
, các triển khai trên sẽ không được tối ưu hóa theo mặc định vì sử dụng onClick={e => doSomething()}
hoặc liên kết trực tiếp trong giai đoạn kết xuất, bởi vì nó sẽ tạo ra một chức năng mới mỗi khi cha mẹ kết xuất. Nếu đây là một nút cổ chai hoàn hảo trong ứng dụng của bạn, bạn có thể truyền dữ liệu cho trẻ em và từ chối nó trong cuộc gọi lại "ổn định" (được đặt trên lớp cha và liên kết với this
trình xây dựng lớp) để PureComponent
tối ưu hóa có thể khởi động hoặc bạn có thể tự thực hiện shouldComponentUpdate
và bỏ qua cuộc gọi lại trong kiểm tra so sánh đạo cụ.
Bạn cũng có thể sử dụng thư viện Recompose , cung cấp các thành phần bậc cao hơn để đạt được tối ưu hóa tinh chỉnh:
// 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)
Trong trường hợp này, bạn có thể tối ưu hóa thành phần Con bằng cách sử dụng:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)