میخوام یه راه درست پیدا کنم که بشه یه سری کامپوننت تعریف کرد که به شکل عمومی استفاده بشن:
<Parent>
<Child value="1">
<Child value="2">
</Parent>
یه منطقی بین رندر کردن کامپوننتهای والد و فرزند وجود داره، مثلاً میتونی <select> و <option> رو به عنوان یه مثال از این منطق در نظر بگیری.
اینم یه پیادهسازی ساده فقط برای پرسیدن سوال:
var Parent = React.createClass({
doSomething: function(value) {
},
render: function() {
return (<div>{this.props.children}</div>);
}
});
var Child = React.createClass({
onClick: function() {
this.props.doSomething(this.props.value); // doSomething is undefined
},
render: function() {
return (<div onClick={this.onClick}></div>);
}
});
سوال اینه که وقتی از {this.props.children} برای تعریف یه کامپوننت رپ استفاده میکنی، چجوری میتونی یه پراپرتی رو به همه فرزندانش پاس بدی؟
کلون کردن فرزندان با پراپسهای جدید
میتونی از React.Children استفاده کنی تا روی فرزندان حلقه بزنی، بعد هر المنت رو با پراپسهای جدید (که به شکل سطحی مرج میشن) با React.cloneElement کلون کنی.
توی کامنت کد میتونی ببینی چرا این روش رو پیشنهاد نمیکنم.
const Child = ({ childName, sayHello }) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);
function Parent({ children }) {
// ما تابع رو به المنتهای فرزند پاس میدیم
function sayHello(childName) {
console.log(`Hello from ${childName} the child`);
}
const childrenWithProps = React.Children.map(children, child => {
// چک کردن
//isValidElement
راه مطمئنیه و از خطای تایپاسکریپت هم جلوگیری میکنه
if (React.isValidElement(child)) {
return React.cloneElement(child, { sayHello });
}
return child;
});
return <div>{childrenWithProps}</div>
}
function App() {
// این روش کمتر با تایپ اسکریپت فرندلی هست چون به نظر میاد داری
//'Child '
//رو بدون
//'sayHello'
//رندر میکنی
return (
<Parent>
<Child childName="Billy" />
<Child childName="Bob" />
</Parent>
);
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="container"></div>
صدا زدن فرزندان به عنوان یک تابع
بهطور جایگزین، میتونی پراپسها رو به فرزندان از طریق render props پاس بدی. توی این روش، فرزندان (که میتونن نام دیگهای هم داشته باشن) یه تابع هستن که میتونه آرگومانهای مورد نظر رو دریافت کنه و فرزندان واقعی رو برگردونه:
const Child = ({ childName, sayHello }) => (
<button onClick={() => sayHello(childName)}>{childName}</button>
);
function Parent({ children }) {
function sayHello(childName) {
console.log(`Hello from ${childName} the child`);
}
return <div>{children(sayHello)}</div>
}
function App() {
return (
<Parent>
{(sayHello) => (
<>
<Child childName="Billy" sayHello={sayHello} />
<Child childName="Bob" sayHello={sayHello} />
</>
)}
</Parent>
);
}
ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="container"></div>