แก้ไข : ดูตัวอย่างสิ้นสุดสำหรับตัวอย่างที่อัปเดต ES6
คำตอบนี้เพียงจัดการกรณีของความสัมพันธ์พ่อแม่และลูกโดยตรง เมื่อผู้ปกครองและเด็กมีแนวโน้มที่จะเป็นคนกลางให้ตรวจสอบคำตอบนี้นี้
โซลูชันอื่น ๆ ขาดประเด็น
ในขณะที่พวกเขายังทำงานได้ดีคำตอบอื่น ๆ จะหายไปบางสิ่งที่สำคัญมาก
ไม่มีวิธีง่ายๆในการส่งอุปกรณ์ประกอบฉากของเด็กไปยังผู้ปกครองโดยใช้กิจกรรมใน React.js หรือไม่?
ผู้ปกครองมีลูกค้ำยันแล้ว! : ถ้าเด็กมีเสาแสดงว่ามันเป็นเพราะผู้ปกครองให้เสานั้นกับเด็ก! ทำไมคุณถึงต้องการให้เด็กส่งเสาไปยังผู้ปกครองในขณะที่ผู้ปกครองมีเสานั้นอยู่แล้ว?
การใช้งานที่ดีขึ้น
เด็ก : ไม่ต้องซับซ้อนกว่านั้นอีกแล้ว
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
Parent with child child : การใช้ค่าที่ส่งผ่านไปยัง child
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
ผู้ปกครองที่มีรายชื่อเด็ก : คุณยังมีทุกสิ่งที่คุณต้องการในผู้ปกครองและไม่จำเป็นต้องทำให้เด็กมีความซับซ้อนมากขึ้น
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
นอกจากนี้ยังเป็นไปได้ที่จะใช้this.handleChildClick.bind(null,childIndex)
แล้วใช้this.state.childrenData[childIndex]
โปรดทราบว่าเรามีผลผูกพันกับnull
บริบทเนื่องจากไม่เช่นนั้นจะทำซ้ำการเตือนที่เกี่ยวข้องกับระบบการโต้กลับอัตโนมัติ ใช้ null หมายความว่าคุณไม่ต้องการเปลี่ยนบริบทฟังก์ชัน ดูสิ่งนี้ด้วยดูเพิ่มเติม
เกี่ยวกับการห่อหุ้มและการมีเพศสัมพันธ์ในคำตอบอื่น ๆ
นี่เป็นความคิดที่ไม่ดีในแง่ของการมีเพศสัมพันธ์และการห่อหุ้ม:
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} />
}
});
การใช้อุปกรณ์ประกอบฉาก : ตามที่ฉันได้อธิบายไว้ข้างต้นคุณมีอุปกรณ์ประกอบฉากในตัวแม่อยู่แล้วดังนั้นจึงไม่มีประโยชน์ที่จะส่งอุปกรณ์ประกอบทั้งหมดของเด็กเพื่อเข้าถึงอุปกรณ์ประกอบฉาก
การใช้ผู้อ้างอิง : คุณมีเป้าหมายการคลิกอยู่แล้วในเหตุการณ์และในกรณีนี้ก็เพียงพอแล้ว นอกจากนี้คุณสามารถใช้การอ้างอิงกับเด็กได้โดยตรง:
<Child ref="theChild" .../>
และเข้าถึงโหนด DOM ในพาเรนต์ด้วย
React.findDOMNode(this.refs.theChild)
สำหรับกรณีขั้นสูงที่คุณต้องการเข้าถึงการอ้างอิงหลายรายการของเด็กในพาเรนต์เด็กสามารถส่งผ่านโหนด dom ทั้งหมดในการเรียกกลับโดยตรง
ส่วนประกอบมีอินเทอร์เฟซ (อุปกรณ์ประกอบฉาก) และพาเรนต์ไม่ควรสมมติสิ่งใด ๆ เกี่ยวกับการทำงานภายในของเด็กรวมถึงโครงสร้าง DOM ภายในหรือโหนด DOM ที่ประกาศการอ้างอิง ผู้ปกครองที่ใช้การอ้างอิงของเด็กหมายความว่าคุณจับคู่คอมโพเนนต์ทั้งสองไว้แน่น
เพื่อแสดงให้เห็นถึงปัญหาฉันจะใช้คำพูดนี้เกี่ยวกับShadow DOMที่ใช้ภายในเบราว์เซอร์เพื่อแสดงสิ่งต่าง ๆ เช่นแถบเลื่อนแถบเลื่อนเครื่องเล่นวิดีโอ ... :
พวกเขาสร้างขอบเขตระหว่างสิ่งที่คุณผู้พัฒนาเว็บสามารถเข้าถึงและสิ่งที่ถือว่าเป็นรายละเอียดการใช้งานจึงไม่สามารถเข้าถึงคุณได้ อย่างไรก็ตามเบราว์เซอร์สามารถข้ามขอบเขตนี้ได้ตามต้องการ ด้วยขอบเขตนี้พวกเขาจึงสามารถสร้างองค์ประกอบ HTML ทั้งหมดโดยใช้เทคโนโลยีเว็บแบบเก่าที่ดีออกไปจาก div และ spans เหมือนกับที่คุณต้องการ
ปัญหาคือว่าถ้าคุณปล่อยให้รายละเอียดการใช้งานของเด็กรั่วไหลไปยังผู้ปกครองคุณทำให้ยากที่จะ refactor เด็กโดยไม่ส่งผลกระทบต่อผู้ปกครอง ซึ่งหมายความว่าในฐานะผู้เขียนไลบรารี (หรือในฐานะผู้แก้ไขเบราว์เซอร์ที่มี Shadow DOM) สิ่งนี้เป็นสิ่งที่อันตรายมากเพราะคุณปล่อยให้ไคลเอ็นต์เข้าถึงมากเกินไปทำให้ยากต่อการอัพเกรดรหัสโดยไม่ทำลายความเข้ากันได้ย้อนหลัง
หาก Chrome ได้นำแถบเลื่อนไปใช้เพื่อให้ไคลเอ็นต์เข้าถึงโหนด dom ด้านในของแถบเลื่อนนั้นหมายความว่าไคลเอ็นต์อาจมีความเป็นไปได้ที่จะทำลายแถบเลื่อนนั้นและแอปนั้นจะแตกง่ายขึ้นเมื่อ Chrome ทำการอัปเดตอัตโนมัติหลังจากทำการเปลี่ยนใหม่ แถบเลื่อน ... แต่พวกเขาให้การเข้าถึงสิ่งที่ปลอดภัยบางอย่างเท่านั้นเช่นการปรับแต่งบางส่วนของแถบเลื่อนด้วย CSS
เกี่ยวกับการใช้อย่างอื่น
ผ่านองค์ประกอบทั้งหมดในการเรียกกลับเป็นอันตรายและอาจนำไปสู่การพัฒนาสามเณรที่จะทำสิ่งที่แปลกมาก ๆ เช่นการโทรchildComponent.setState(...)
หรือchildComponent.forceUpdate()
หรือกำหนดตัวแปรใหม่ภายในแม่ทำให้แอปทั้งยากมากที่จะเกี่ยวกับเหตุผล
แก้ไข: ตัวอย่าง ES6
ในขณะที่หลาย ๆ คนใช้ ES6 ต่อไปนี้เป็นตัวอย่างเดียวกันสำหรับไวยากรณ์ ES6
เด็กนั้นง่ายมาก:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
ผู้ปกครองสามารถเป็นได้ทั้งคลาส (และในที่สุดก็สามารถจัดการสถานะของตัวเอง แต่ฉันผ่านมันเป็นอุปกรณ์ประกอบฉากที่นี่:
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>
);
}
}
แต่ก็สามารถลดความซับซ้อนได้หากไม่จำเป็นต้องจัดการสถานะ:
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 WARNING (นำไปใช้กับ ES5 / ES6): หากคุณกำลังใช้งานPureComponent
หรือshouldComponentUpdate
การใช้งานด้านบนจะไม่ได้รับการปรับให้เหมาะสมโดยค่าเริ่มต้นเนื่องจากการใช้onClick={e => doSomething()}
หรือการผูกโดยตรงระหว่างขั้นตอนการเรนเดอร์เพราะมันจะสร้างฟังก์ชันใหม่ทุกครั้ง หากเป็นคอขวด perf ใน app ของคุณคุณสามารถส่งผ่านข้อมูลไปยังเด็กและ reinject มันภายใน "คงที่" โทรกลับ (ตั้งอยู่บนชั้นผู้ปกครองและ binded ไปthis
ในตัวสร้าง class) เพื่อให้PureComponent
การเพิ่มประสิทธิภาพสามารถเตะในหรือคุณ สามารถใช้ของคุณเองshouldComponentUpdate
และละเว้นการโทรกลับในการตรวจสอบการเปรียบเทียบอุปกรณ์ประกอบฉาก
นอกจากนี้คุณยังสามารถใช้Recompose ไลบรารี่ซึ่งให้ส่วนประกอบการสั่งซื้อที่สูงขึ้นเพื่อให้เกิดประสิทธิภาพสูงสุด:
// 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)
ในกรณีนี้คุณสามารถเพิ่มประสิทธิภาพองค์ประกอบเด็กโดยใช้:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)