Нет ли простого способа передать дочерний элемент props
его родительскому объекту, используя события, в React.js?
var Child = React.createClass({
render: function() {
<a onClick={this.props.onClick}>Click me</a>
}
});
var Parent = React.createClass({
onClick: function(event) {
// event.component.props ?why is this not available?
},
render: function() {
<Child onClick={this.onClick} />
}
});
Я знаю, что вы можете использовать контролируемые компоненты для передачи входного значения, но было бы неплохо передать весь комплект n 'kaboodle. Иногда дочерний компонент содержит набор информации, которую вам больше не нужно искать.
Может быть, есть способ привязать компонент к событию?
После использования React в течение года и под влиянием ответа Себастьяна Лорбера, я завершил передачу дочерних компонентов, поскольку аргументы в отношении функций в родителях на самом деле не являются способом React, и не было хорошей идеей. Я переключил ответ.
Изменить: см. примеры примеров для обновленных примеров 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);
}
});
Родитель со списком детей: у вас все еще есть все, что вам нужно для родителя, и не нужно делать ребенка более сложным.
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);
}
});
Также можно использовать this.handleChildClick.bind(null,childIndex)
, а затем использовать this.state.childrenData[childIndex]
Обратите внимание, что мы привязываемся к контексту null
, потому что иначе React выдает предупреждение, связанное с его системой autobinding. Использование null означает, что вы не хотите изменять контекст функции. См. также.
Это для меня идея bad в отношении связи и инкапсуляции:
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} />
}
});
Использование реквизитов: Как я уже объяснял выше, у вас уже есть реквизит в родителе, поэтому бесполезно передавать весь дочерний компонент для доступа к реквизитам.
Использование ссылок: У вас уже есть цель клика в событии, и в большинстве случаев этого достаточно. Кроме того, вы могли бы использовать ref непосредственно для ребенка:
<Child ref="theChild" .../>
И получите доступ к DOM node в родительском каталоге с
React.findDOMNode(this.refs.theChild)
Для более сложных случаев, когда вы хотите получить доступ к нескольким refs дочернего элемента в родительском, ребенок может передать все dom-узлы непосредственно в обратном вызове.
Компонент имеет интерфейс (реквизит), и родительский объект не должен принимать во внимание внутреннюю работу дочернего элемента, включая его внутреннюю структуру DOM или какие узлы DOM, для которых он объявляет refs. Родитель, использующий ссылку ref для ребенка, означает, что вы плотно соединяете 2 компонента.
Чтобы проиллюстрировать эту проблему, я приму эту цитату о Shadow DOM, который используется внутри браузеров для отображения таких вещей, как ползунки, полосы прокрутки, видеоплееры...:
Они создали границу между тем, что вы, веб-разработчик, можете достичь и что рассмотрели детали реализации, поэтому недоступны для вы. Однако браузер может переходить через эту границу по своему усмотрению. С этой границей они смогли построить все элементы HTML используя те же самые добрые веб-технологии, из разделов и пролетов как и вы.
Проблема заключается в том, что если вы позволите дочерним элементам реализации проникнуть в родительский элемент, вы сделаете очень сложным рефакторинг ребенка, не затрагивая родителя. Это означает, что в качестве автора библиотеки (или в качестве редактора браузера с 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>
)
ПРЕДУПРЕЖДЕНИЕ PERF (примените к 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 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)
props
. Согласен?
How do I know which of my children was chosen among a group?
на самом деле это не часть первоначального вопроса, но я включил решение в свой ответ. В отличие от вашего редактирования, я думаю, что совсем не нужно изменять дочерний объект, даже когда он работает со списком, поскольку вы можете использовать bind
непосредственно в родительском элементе.
Обновление (9/1/15): ОП поставил этот вопрос немного движущейся мишенью. Его снова обновили. Поэтому я отвечаю, чтобы обновить свой ответ.
Да, это возможно.
Это можно решить, обновив Childs onClick
как this.props.onClick.bind(null, this)
:
var Child = React.createClass({
render: function () {
return <a onClick={this.props.onClick.bind(null, this)}>Click me</a>;
}
});
Обработчик событий в вашем родителе может затем получить доступ к компоненту и событию следующим образом:
onClick: function (component, event) {
// console.log(component, event);
},
Родитель уже знает Childs props
.
Это не ясно в приведенном примере, поскольку на самом деле никаких реквизитов не предоставляется. Этот пример кода может лучше поддерживать задаваемый вопрос:
var Child = React.createClass({
render: function () {
return <a onClick={this.props.onClick}> {this.props.text} </a>;
}
});
var Parent = React.createClass({
getInitialState: function () {
return { text: "Click here" };
},
onClick: function (event) {
// event.component.props ?why is this not available?
},
render: function() {
return <Child onClick={this.onClick} text={this.state.text} />;
}
});
В этом примере становится намного понятнее, что вы уже знаете, каковы реквизиты Ребенка.
Если он действительно использует реквизиты Childs, вы можете вообще избежать взаимодействия с ребенком.
JSX имеет атрибуты распространения API, который я часто использую на таких компонентах, как Child. Он принимает все props
и применяет их к компоненту. Ребенок будет выглядеть так:
var Child = React.createClass({
render: function () {
return <a {...this.props}> {this.props.text} </a>;
}
});
Позволяет использовать значения непосредственно в родительском устройстве:
var Parent = React.createClass({
getInitialState: function () {
return { text: "Click here" };
},
onClick: function (text) {
alert(text);
},
render: function() {
return <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />;
}
});
И нет необходимости в дополнительной настройке при подключении дополнительных дочерних компонентов
var Parent = React.createClass({
getInitialState: function () {
return {
text: "Click here",
text2: "No, Click here",
};
},
onClick: function (text) {
alert(text);
},
render: function() {
return <div>
<Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />
<Child onClick={this.onClick.bind(null, this.state.text2)} text={this.state.text2} />
</div>;
}
});
Но я подозреваю, что это не ваш фактический прецедент. Так что давайте копаем дальше...
Общий характер представленного примера трудно обсуждать. Ive создал компонент, который демонстрирует практическое использование для вышеупомянутого вопроса, реализованного очень эффективным образом:
Рабочий пример DTServiceCalculator
DTServiceCalculator repo
Этот компонент является простым калькулятором обслуживания. Вы предоставляете ему список услуг (с именами и ценами), и он рассчитает общее количество выбранных цен.
ServiceItem
является дочерним компонентом в этом примере. У него нет много мнений о внешнем мире. Для него требуется несколько реквизитов, одна из которых - это функция, которая будет вызываться при нажатии.
<div onClick={this.props.handleClick.bind(this.props.index)} />
Он делает только вызов вызываемого handleClick
обратного вызова с предоставленным index
[source].
DTServicesCalculator
является родительским компонентом в этом примере. Это тоже ребенок. Посмотрим.
DTServiceCalculator
создает список дочернего компонента (ServiceItem
s) и предоставляет им реквизиты [source]. Его родительский компонент ServiceItem
, но он является дочерним компонентом компонента, передающим ему список. Он не владеет данными. Поэтому он снова делегирует обработку компонента его родительскому компоненту source
<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem} />
handleServiceItem
фиксирует индекс, переданный из дочернего элемента, и предоставляет его родительскому элементу < source]
handleServiceClick (index) {
this.props.onSelect(index);
}
Концепция "Собственность" является важной в Реактировании. Я рекомендую прочитать больше об этом здесь.
В приведенном выше примере Ive я продолжаю делегировать обработку события до дерева компонентов до тех пор, пока мы не перейдем к компоненту, которому принадлежит состояние.
Когда мы, наконец, доберемся туда, мы обрабатываем выбор/отключение состояния так [source]:
handleSelect (index) {
let services = […this.state.services];
services[index].chosen = (services[index].chosen) ? false : true;
this.setState({ services: services });
}
Постарайтесь, чтобы ваши внешние компоненты были максимально непрозрачными. Стремитесь убедиться, что у них очень мало предпочтений относительно того, как родительский компонент может выбрать их реализовать.
Имейте в виду, кто владеет данными, которыми вы управляете. В большинстве случаев вам необходимо делегировать обработку событий по дереву компоненту, которому принадлежит это состояние.
Кроме того: Flux pattern - хороший способ уменьшить этот тип необходимого подключения в приложениях.
Parent
может представлять собой такой Controller-View, а Child
- просто dumb-View (компонент). Только Controller-Views должны иметь знания о приложении. В этом случае вы все равно передадите Действие от Parent
Child
в качестве опоры. Что касается самого действия, Flux имеет строго предписанный шаблон для взаимодействия между действиями для обновления представлений. facebook.github.io/flux/docs/...
Появляется простой ответ. Рассмотрим это:
var Child = React.createClass({
render: function() {
<a onClick={this.props.onClick.bind(null, this)}>Click me</a>
}
});
var Parent = React.createClass({
onClick: function(component, event) {
component.props // #=> {Object...}
},
render: function() {
<Child onClick={this.onClick} />
}
});
Ключ вызывает bind(null, this)
в событии this.props.onClick
, переданном из родителя. Теперь функция onClick принимает аргументы component
, AND event
. Я думаю, что лучший из всех миров.
Это была плохая идея: позволить деталям реализации ребенка течь в родителя никогда не был хорошим путем. См. Ответ Себастьяна Лорбер.
this
внутри функции (которая в любом случае не нужна), а второй добавляется в качестве первого аргумента при вызове onClick.
Вопрос заключается в том, как передать аргумент от дочернего к родительскому компоненту. Этот пример прост в использовании и проверке:
//Child component
class Child extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
)
}
}
//Parent component
class Parent extends React.Component {
constructor(props) {
super(props);
var handleToUpdate = this.handleToUpdate.bind(this);
}
handleToUpdate(someArg){
alert('We pass argument from Child to Parent: \n' + someArg);
}
render() {
var handleToUpdate = this.handleToUpdate;
return (<div>
<Child handleToUpdate = {handleToUpdate.bind(this)} /></div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<Parent />,
document.querySelector("#demo")
);
}
В основном вы используете реквизиты для отправки информации от и от Child и Parent.
Добавляя все замечательные ответы, позвольте мне привести простой пример, объясняющий передачу значений от дочернего к родительскому компоненту в React
App.js
class App extends React.Component {
constructor(){
super();
this.handleFilterUpdate = this.handleFilterUpdate.bind(this);
this.state={name:'igi'}
}
handleFilterUpdate(filterValue) {
this.setState({
name: filterValue
});
}
render() {
return (
<div>
<Header change={this.handleFilterUpdate} name={this.state.name} />
<p>{this.state.name}</p>
</div>
);
}
}
Header.js
class Header extends React.Component {
constructor(){
super();
this.state={
names: 'jessy'
}
}
Change(event) {
// this.props.change(this.state.names);
this.props.change('jessy');
}
render() {
return (
<button onClick={this.Change.bind(this)}>click</button>
);
}
}
Main.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
ReactDOM.render(<App />, document.getElementById('app'));
Это означает, что теперь вы можете передавать значения с вашего клиента на сервер.
Взгляните на функцию изменения в Header.js
Change(event) {
// this.props.change(this.state.names);
this.props.change('jessy');
}
Вот как вы вставляете значения в реквизиты с клиента на сервер
Вот простая трехэтапная реализация ES6 с использованием привязки функций в родительском конструкторе. Это первый способ, рекомендуемый официальным учебным пособием (существует также синтаксис полей открытых классов, который здесь не рассматривается). Вы можете найти всю эту информацию здесь https://reactjs.org/docs/handling-events.html
Связывание родительских функций, чтобы дети могли их вызвать (и передать данные до родителя!: D)
Родительская функция
handleFilterApply(filterVals){}
Родительский конструктор
this.handleFilterApply = this.handleFilterApply.bind(this);
Prop, переданный ребенку
onApplyClick = {this.handleFilterApply}
Вызов детского события
onClick = {() => {props.onApplyClick(filterVals)}