Изменить : см. Конечные примеры для обновленных примеров 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 или о том, для каких узлов DOM он объявляет ссылки. Родитель, использующий ссылку на ребенка, означает, что вы тесно связываете 2 компонента.
Чтобы проиллюстрировать проблему, я возьму цитату о Shadow DOM , которая используется внутри браузеров для рендеринга таких вещей, как ползунки, полосы прокрутки, видеоплееры ...:
Они создали границу между тем, что вы, веб-разработчик, можете достичь, и тем, что считается деталями реализации, поэтому недоступны для вас. Браузер, однако, может проходить через эту границу по желанию. Имея эту границу, они смогли построить все элементы HTML, используя те же самые старые добрые веб-технологии, из элементов div и span, как и вы.
Проблема заключается в том, что если вы позволите деталям дочерней реализации проникнуть в родительский объект, вам будет очень сложно провести рефакторинг дочернего элемента, не затрагивая родительский элемент. Это означает, что для автора библиотеки (или для редактора браузера с 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
ПРЕДУПРЕЖДЕНИЕ О ПРОИЗВОДИТЕЛЬНОСТИ (применимо к ES5 / ES6): если вы используете PureComponent
или shouldComponentUpdate
, вышеприведенные реализации не будут оптимизированы по умолчанию из-за использования onClick={e => doSomething()}
или привязки непосредственно во время фазы рендеринга, потому что она будет создавать новую функцию каждый раз при рендеринге родительского объекта. Если это узкое место в вашем приложении, вы можете передать данные дочерним элементам и повторно внедрить их в «стабильный» обратный вызов (установленный в родительском классе и связанный с this
конструктором класса), чтобы 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)