Передайте реквизиты родительскому компоненту в файле React.js

Нет ли простого способа передать дочерний элемент 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. Иногда дочерний компонент содержит набор информации, которую вам больше не нужно искать.

Может быть, есть способ привязать компонент к событию?

ОБНОВЛЕНИЕ - 9/1/2015

После использования React в течение года и под влиянием ответа Себастьяна Лорбера, я завершил передачу дочерних компонентов, поскольку аргументы в отношении функций в родителях на самом деле не являются способом React, и не было хорошей идеей. Я переключил ответ.

Ответ 1

Изменить: см. примеры примеров для обновленных примеров 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 выдает предупреждение, связанное с его системой 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>
)

JsFiddle


ПРЕДУПРЕЖДЕНИЕ 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)

Ответ 2

Обновление (1/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>;
  }
});

Затем обработчик события в вашем Parent может получить доступ к компоненту и событию следующим образом:

  onClick: function (component, event) {
    // console.log(component, event);
  },

Снимок JSBin


Но сам вопрос вводит в заблуждение

Родитель уже знает 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} />;
  }
});

В этом примере становится намного понятнее, что вы уже знаете, что такое реквизит ребенка.

Снимок JSBin


Если это действительно об использовании реквизита Чайлдс...

Если речь идет об использовании реквизита Childs, вы можете полностью исключить любое взаимодействие с Child.

JSX имеет API атрибутов распространения, который я часто использую в таких компонентах, как Child. Он берет все props и применяет их к компоненту. Ребенок будет выглядеть так:

var Child = React.createClass({
  render: function () {
    return <a {...this.props}> {this.props.text} </a>;
  }
});

Позволяет вам использовать значения непосредственно в Parent:

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} />;
  }
});

Снимок JSBin


И при подключении дополнительных дочерних компонентов дополнительная настройка не требуется.

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>;
  }
});

Снимок JSBin

Но я подозреваю, что это не ваш фактический вариант использования. Итак, давайте копать дальше...


Надежный практический пример

Об общем характере предоставленного примера трудно говорить. Я создал компонент, который демонстрирует практическое использование вышеуказанного вопроса, реализованный очень Reacty способом:

Рабочий пример DTServiceCalculator
DTServiceCalculator РЕПО

Этот компонент представляет собой простой сервисный калькулятор. Вы предоставляете ему список услуг (с именами и ценами), и он рассчитывает итоговые цены.

Дети блаженно невежественны

ServiceItem является дочерним компонентом в этом примере. Он не имеет много мнений о внешнем мире. Требуется несколько реквизитов, один из которых - функция, вызываемая при нажатии.

<div onClick={this.props.handleClick.bind(this.props.index)}/>

Он ничего не делает, кроме как вызывает предоставленный handleClick вызов handleClick с предоставленным index [ source ].

Родители дети

DTServicesCalculator является родительским компонентом этого примера. Это также ребенок. Давайте смотреть.

DTServiceCalculator создает список дочерних компонентов (ServiceItem) и предоставляет им реквизиты [ источник ]. Это родительский компонент ServiceItem но это дочерний компонент компонента, передающего ему список. Он не владеет данными. Так что еще раз делегаты обращение компоненты к родительскому-компоненту источнику

<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem}/>

handleServiceItem захватывает индекс, переданный от дочернего, и предоставляет его своему родителю [ source ]

handleServiceClick (index) {
  this.props.onSelect(index);
}

Владельцы знают все

Концепция "собственности" является важной в React. Я рекомендую прочитать больше об этом здесь.

В примере, который я показал, я продолжаю делегировать обработку события в дереве компонентов, пока не доберемся до компонента, которому принадлежит состояние.

Когда мы наконец доберемся туда, мы обработаем выбор/отмену выбора состояния следующим образом: [ source ]:

handleSelect (index) {
  let services = […this.state.services];
  services[index].chosen = (services[index].chosen) ? false : true;
  this.setState({ services: services });
}


Заключение

Постарайтесь, чтобы ваши внешние компоненты были максимально непрозрачными. Старайтесь убедиться, что у них очень мало предпочтений относительно того, как родительский компонент может их реализовать.

Имейте в виду, кто владеет данными, которыми вы манипулируете. В большинстве случаев вам нужно будет делегировать обработку события по дереву компоненту, которому принадлежит это состояние.

Кроме того: паттерн Flux - это хороший способ уменьшить количество необходимых подключений в приложениях.

Ответ 3

Появляется простой ответ. Рассмотрим это:

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. Я думаю, что лучший из всех миров.

ОБНОВЛЕНИЕ: 9/1/2015

Это была плохая идея: позволить деталям реализации ребенка течь в родителя никогда не был хорошим путем. См. Ответ Себастьяна Лорбер.

Ответ 4

Вопрос в том, как передать аргумент от дочернего к родительскому компоненту. Этот пример прост в использовании и проверен:

//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")
    );
}

Посмотрите на JSFIDDLE

Ответ 5

В основном вы используете реквизиты для отправки информации от и от 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');
  }

Вот как вы вставляете значения в реквизиты с клиента на сервер

Ответ 6

Вот простая трехэтапная реализация ES6 с использованием привязки функций в родительском конструкторе. Это первый способ, рекомендуемый официальным учебным пособием (существует также синтаксис полей открытых классов, который здесь не рассматривается). Вы можете найти всю эту информацию здесь https://reactjs.org/docs/handling-events.html

Связывание родительских функций, чтобы дети могли их вызвать (и передать данные до родителя!: D)

  • Убедитесь, что в родительском конструкторе вы связываете функцию, созданную в родительском
  • Передайте связанную функцию вниз ребенку в качестве пропеллера (нет лямбда, потому что мы передаем функцию ref)
  • Вызов связанной функции из дочернего события (Lambda! Мы вызываем функцию при запуске события.   Если мы этого не сделаем, функция будет автоматически запускаться при загрузке и не запускаться в событии.)

Родительская функция

handleFilterApply(filterVals){} 

Родительский конструктор

this.handleFilterApply = this.handleFilterApply.bind(this);

Prop, переданный ребенку

onApplyClick = {this.handleFilterApply}

Вызов детского события

onClick = {() => {props.onApplyClick(filterVals)}

Ответ 7

В настоящее время можно использовать более легкий способ. 2-минутное ВИДЕО дает объяснение кода

Parent and child components