Редагувати : див. Кінцеві приклади оновлених прикладів ES6.
Ця відповідь просто стосується випадку прямих стосунків батько-дитина. Коли у батька та дитини потенційно багато посередників, перевірте цю відповідь .
В інших рішеннях не вистачає сенсу
Хоча вони все ще працюють добре, в інших відповідях пропускається щось дуже важливе.
Чи не існує простого способу передавати реквізит дитині своєму батькові, використовуючи події, в React.js?
У батька вже є ця дочірня підтримка! : якщо у дитини є опора, то це тому, що її батько надав таку підтримку дитині! Чому ви хочете, щоб дитина повернула опору батькові, тоді як батько, очевидно, вже має цю опору?
Краще впровадження
Дитина : насправді це не повинно бути складніше за це.
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
Батько з одинокою дитиною : використовуючи значення, яке воно передає дитині
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
контекстом, оскільки в іншому випадку React надсилає попередження, пов’язане з його системою автозахисту . Використання 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 він оголошує реф. Батько, який використовує реферат дитини, означає, що ви щільно з'єднаєте 2 компоненти.
Для ілюстрації проблеми я візьму цю цитату про Shadow DOM , який використовується у веб-переглядачах для відображення таких речей, як слайдери, смуги прокрутки, відеоплеєри ...:
Вони створили межу між тим, що ви, веб-розробник, можете досягти, і тим, що вважається деталями реалізації, таким чином недоступним для вас. Браузер, однак, може перейти через цю межу за бажанням. Завдяки цій межі вони змогли створити всі HTML-елементи за допомогою тих самих старих веб-технологій із знаків і прольотів, як і ви.
Проблема полягає в тому, що якщо ви дозволите, щоб деталі реалізації дитини просочилися до батьків, вам дуже важко переробити дитину, не впливаючи на батьків. Це означає, що як автор бібліотеки (або як редактор браузера з Shadow DOM) це дуже небезпечно, оскільки ви дозволяєте клієнту надто багато доступу, що робить його дуже важким для оновлення коду, не порушуючи ретросумісності.
Якщо Chrome реалізував смугу прокрутки, дозволяючи клієнту отримувати доступ до внутрішніх вузлів дому цієї смуги прокрутки, це означає, що у клієнта може бути можливість просто зламати цю смугу прокрутки, і що додатки зруйнуються легше, коли 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 ПОПЕРЕДЖЕННЯ (застосовується до ES5 / ES6): якщо ви використовуєте PureComponent
або shouldComponentUpdate
, вищезазначені реалізації не будуть оптимізовані за замовчуванням, оскільки використання onClick={e => doSomething()}
або зв'язування безпосередньо під час фази візуалізації, оскільки це створюватиме нову функцію щоразу, коли батько надає. Якщо це вузьке вузьке місце у вашому додатку, ви можете передавати дані дітям та повторно вводити їх у "стабільний" зворотний виклик (встановлений у батьківському класі та прив’язаний до this
конструктора класів), щоб PureComponent
оптимізація могла спричинити вас, або ви може реалізовувати свій власний shouldComponentUpdate
і ігнорувати зворотний виклик під час перевірки порівняння реквізиту.
Ви також можете скористатись бібліотекою " Рекомендувати" , яка забезпечує компоненти більш високого порядку для досягнення тонких налаштувань:
// 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)