Связывание компонентов React с использованием props.childern [duplicate]

Вы могли бы сделать несколько вещей. Один из примеров - это элементы в списке Animal

Используя ваш код:

  cat.Play (  новый список & lt; Cat & gt; (). Cast & lt; Animal & gt; (). ToList ());   

Другим является создание Animal generic, поэтому cat.Play (новый List & lt; Cat & gt; ()); будет работать. [ ! d13]

  класс Animal & lt; T & gt;  {public virtual void Play (List & lt; T & gt; животные) {}} класс Cat: Animal & lt; Cat & gt;  {public override void Play (List & lt; Cat & gt; cats) {}} class Program {static void Main (string [] args) {Cat cat = new Cat ();  cat.Play (новый список & lt; Cat & gt; ());  }}  

Другим способом является не создание Animal generic, а метод Play и ограничение этого на T: Animal

  класс Animal {public virtual void Play & lt; T & gt; (List & lt; T & gt; животные), где T: Animal {}} класс Cat: Animal {public override void Play & lt; T & gt;  (List & lt; T & gt; животные) {}}  

Наконец, если вы находитесь на C # 4 и вам нужно только перечислить список и не изменять его, проверьте ответ Эрика Липперта на IEnumerable & л; Животное & GT;!.! [D9] [D15]

564
задан Tahir Ahmed 3 September 2015 в 09:23
поделиться

19 ответов

Вы можете использовать React.Children для итерации над дочерними элементами, а затем клонировать каждый элемент новыми реквизитами (неглубоким объединением), используя React.cloneElement , например:

const Child = ({ doSomething, value }) => (
  <div onClick={() => doSomething(value)}>Click Me</div>
);

class Parent extends React.PureComponent {
  doSomething = (value) => {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    const { children } = this.props;

    const childrenWithProps = React.Children.map(children, child =>
      React.cloneElement(child, { doSomething: this.doSomething }));

    return <div>{childrenWithProps}</div>
  }
};

ReactDOM.render(
  <Parent>
    <Child value="1" />
    <Child value="2" />
  </Parent>,
  document.getElementById('container')
);

Fiddle: https://jsfiddle.net/2q294y43/2/

628
ответ дан allejo 16 August 2018 в 00:57
поделиться
  • 1
    Это не работает для меня. это не определено в React.cloneElement () – Patrick 8 March 2016 в 04:32
  • 2
    Этот ответ не работает, value, переданный в doSomething, теряется. – Dave 3 March 2017 в 00:03
  • 3
    @Dave вы попробовали запустить скрипку? .. – Dominic Tobias 3 March 2017 в 10:29
  • 4
    @DominicTobias Arg, извините, я переключил console.log, чтобы предупредить и забыл согласовать два параметра с одной строкой. – Dave 4 March 2017 в 17:03
  • 5
    Что делать, если ребенок загружается через маршрут (v4), загруженный с отдельной страницы маршрута? – blamb 13 March 2018 в 02:33

Попробуйте это

<div>{React.cloneElement(this.props.children, {...this.props})}</div>

Он работал для меня с помощью реакции-15.1.

59
ответ дан 7puns 16 August 2018 в 00:57
поделиться
  • 1
    Можно ли возвращать React.cloneElement() напрямую, не окружая его в тегах <div>? Потому что, если ребенок является <span> (или что-то еще), и мы хотим сохранить его тип элемента тега? – adrianmc 17 December 2016 в 04:45
  • 2
    Если это один ребенок, вы можете оставить оболочку, и это решение работает только для одного ребенка, так что да. – ThaJay 15 March 2017 в 12:48
  • 3
    Работает на меня. Без включения & lt; div & gt; это нормально. – Crash Override 8 May 2017 в 18:22
  • 4
    Если вам нужно явно указать, что вы получаете только одного ребенка, вы можете сделать React.cloneElement(React.Children.only(this.props.children), {...this.props}), который выдает ошибку, если ему передается более одного ребенка. Тогда вам не нужно обертывать div. – itsananderson 21 August 2017 в 22:05
  • 5
    Этот ответ может вызвать значение TypeError: циклического объекта. Если вы не хотите, чтобы один из реквизитов ребенка был сам, используйте let {children, ...acyclicalProps} = this.props, а затем React.cloneElement(React.Children.only(children), acyclicalProps). – Parabolord 16 June 2018 в 18:57

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

Функция в качестве дочерних компонентов

0
ответ дан Alexandr Cherednichenko 16 August 2018 в 00:57
поделиться

Вы можете использовать React.cloneElement, лучше знать, как это работает, прежде чем вы начнете использовать его в своем приложении. Это введено в React v0.13, читайте дальше для получения дополнительной информации, поэтому что-то вместе с этой работой для вас:

<div>{React.cloneElement(this.props.children, {...this.props})}</div>

Поэтому приведите строки из документации React для вас, чтобы понять, как все это работает и как вы может использовать их:

В React v0.13 RC2 мы представим новый API, похожий на React.addons.cloneWithProps, с этой сигнатурой:

React.cloneElement(element, props, ...children);

В отличие от cloneWithProps, эта новая функция не имеет никакого волшебного встроенного поведения для слияния стиля и className по той же причине, что у нас нет этой функции от transferPropsTo. Никто не уверен, что именно представляет собой полный список волшебных вещей, что затрудняет разузнание кода и трудно повторное использование, когда стиль имеет другую подпись (например, в предстоящем React Native).

React. cloneElement почти эквивалентен:

<element.type {...element.props} {...props}>{children}</element.type>

Однако, в отличие от JSX и cloneWithProps, он также сохраняет ссылки. Это означает, что если вы получите ребенка с рефлексией на нем, вы случайно не украдете его у своего предка. Вы получите тот же ref, что и ваш новый элемент.

Один общий шаблон - это сопоставление с вашими детьми и добавление новой опоры. Было много сообщений о том, что cloneWithProps потерял ref, что затрудняет рассуждение о вашем коде. Теперь, следуя той же схеме с cloneElement, будет работать так, как ожидалось. Например:

var newChildren = React.Children.map(this.props.children, function(child) {
  return React.cloneElement(child, { foo: true })
});

Примечание: React.cloneElement (child, {ref: 'newRef'}) Отменяет ref, поэтому для двух родителей иметь ссылку на одного и того же ребенка, если вы не используете callback-refs.

Это была важная функция, чтобы попасть в React 0.13, поскольку реквизиты теперь неизменяемы. Путь обновления часто клонирует элемент, но при этом вы можете потерять ref. Поэтому нам нужен был более удобный путь обновления. Когда мы обновляли callites в Facebook, мы поняли, что нам нужен этот метод. Мы получили те же отзывы от сообщества. Поэтому мы решили сделать еще один RC перед окончательным выпуском, чтобы убедиться, что мы это получим.

Мы планируем в конечном итоге отказаться от React.addons.cloneWithProps. Мы еще не делаем этого, но это хорошая возможность начать думать о ваших собственных потребностях и вместо этого использовать React.cloneElement. Мы обязательно отправим выпуск с уведомлениями об отказе, прежде чем мы удалим его, поэтому немедленное действие не потребуется.

больше здесь ...

15
ответ дан Alireza 16 August 2018 в 00:57
поделиться

Это то, что вам нужно?

var Parent = React.createClass({
  doSomething: function(value) {
  }
  render: function() {
    return  <div>
              <Child doSome={this.doSomething} />
            </div>
  }
})

var Child = React.createClass({
  onClick:function() {
    this.props.doSome(value); // doSomething is undefined
  },  
  render: function() {
    return  <div onClick={this.onClick}></div>
  }
})
0
ответ дан amit_183 16 August 2018 в 00:57
поделиться
  • 1
    Нет, я не хочу ограничивать содержимое моей обертки каким-то конкретным контентом. – plus- 3 September 2015 в 09:02

Для немного более чистого способа сделать это, попробуйте:

<div>
    {React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })}
</div>

Примечание: это будет работать только в том случае, если есть один ребенок, и это действительный элемент React.

295
ответ дан Andy 16 August 2018 в 00:57
поделиться
  • 1
    Я использовал самый рейтинговый ответ, но этот гораздо более прямолинейный! Это решение также используется ими на странице примеров реактивных маршрутизаторов. – captDaylight 9 March 2016 в 15:30
  • 2
    Может ли кто-нибудь объяснить, как это работает (или что это на самом деле)? Чтение документов , я не мог понять, как это спустится в детей и добавить эту опору для каждого ребенка - это то, что она предназначена? Если да, то откуда мы знаем, что это будет сделано? Совершенно очевидно, что даже для пропуска непрозрачной структуры данных (this.props.children) до cloneElement ..., которая ожидает элемент ..., даже допустимо. – GreenAsJade 28 March 2016 в 21:55
  • 3
    Именно это, похоже, не работает с более чем одним ребенком. – Danita 29 March 2016 в 12:22
  • 4
    Таким образом, вы можете писать код, который работает, когда кто-то передает только один ребенок в компонент, но когда он добавляет другой, он сбой ... что не звучит отлично по номиналу? Казалось бы, это ловушка для OP, которая специально спросила о передаче реквизита детям all . – GreenAsJade 5 May 2016 в 12:23
  • 5
    @GreenAsJade его отлично, пока ваш компонент ожидает одного ребенка. Вы можете определить через свои компоненты propTypes, что он ожидает одного ребенка. Функция React.Children.only возвращает единственного дочернего элемента или выбрасывает исключение, если его несколько (это не было бы, если бы не был случай использования). – cchamberlain 5 May 2016 в 18:14

Некоторая причина, по которой React.children не работал на меня. Это то, что сработало для меня.

Я хотел просто добавить класс к ребенку. аналогично изменению prop

 var newChildren = this.props.children.map((child) => {
 const className = "MenuTooltip-item " + child.props.className;
    return React.cloneElement(child, { className });
 });

 return <div>{newChildren}</div>;

Трюк здесь - React.cloneElement . Вы можете передать любую опору аналогичным образом

0
ответ дан aWebDeveloper 16 August 2018 в 00:57
поделиться

С обновлением для React 16.3 теперь вы можете использовать React.createContext.

import * as React from 'react';

// React.createContext accepts a defaultValue as the first param
const { Provider: ParentProvider, Consumer: ChildConsumer } = React.createContext(); 

class Parent extends React.Component {
  doSomething = (value) => {
    // Do something here with value
  };

  render() {
    return (
       <ParentProvider value={{ onClick: this.doSomething }}>
         {this.props.children}
       </ParentProvider>
    );
  }
}

class Child extends React.Component {
  render() {
    const { value } = this.props;
    return (
       <ChildConsumer> 
         (({ doSomething }) => {
           return <div onClick={() => doSomething(value)}>{value}</div>
         })
       </ChildConsumer>
    );
  }
}


// Example of using Parent and Child

import * as React from 'react';

class SomeComponent extends React.Component {

  render() {
    <Parent>
      <Child value={1} />
      <Child value={2} />
    </Parent>
  }
}

React.createContext сияет, где case React.cloneElement не может обрабатывать вложенные компоненты

class SomeComponent extends React.Component {

  render() {
    <Parent>
      <Child value={1} />
      <SomeOtherComp><Child value={2} /></SomeOtherComp>
    </Parent>
  }
}
12
ответ дан Kenneth Truong 16 August 2018 в 00:57
поделиться

Пропустить реквизит для направления детей.

Посмотреть все другие ответы

Пропустить общие глобальные данные через дерево компонентов через контекст

< blockquote>

Контекст предназначен для обмена данными, которые можно считать «глобальными» для дерева компонентов React, таких как текущий аутентифицированный пользователь, тема или предпочтительный язык. 1

Отказ от ответственности: это обновленный ответ, предыдущий использовал старый контекстный API

Он основан на принципе Consumer / Provide , Во-первых, создайте свой контекст

const { Provider, Consumer } = React.createContext(defaultValue);

Затем используйте через

<Provider value={/* some value */}>
  {children} /* potential consumers */
<Provider />

и

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>

Все потребители, являющиеся потомками Провайдера будет повторно отображаться всякий раз, когда изменяется значение поддержки поставщика. Распространение от Провайдера к его Потребителям-потомкам не подлежит методу shouldComponentUpdate, поэтому потребитель обновляется даже тогда, когда компонент-предок освобождается от обновления. 1

Полный пример, полу-псевдокод.

import React from 'react';

const { Provider, Consumer } = React.createContext({ color: 'white' });

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: { color: 'black' },
    };
  }

  render() {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    );
  }
}

class Toolbar extends React.Component {
  render() {
    return ( 
      <div>
        <p> Consumer can be arbitrary levels deep </p>
        <Consumer> 
          {value => <p> The toolbar will be in color {value.color} </p>}
        </Consumer>
      </div>
    );
  }
}

1 https://facebook.github.io/react/docs/context. HTML

39
ответ дан lustoykov 16 August 2018 в 00:57
поделиться
  • 1
    В отличие от принятого ответа, это будет работать правильно, даже если есть другие элементы, входящие в состав Parent. Это, безусловно, лучший ответ. – Zaptree 11 September 2016 в 23:36
  • 2
    Использование контекста не рекомендуется, если только он не является единственным. facebook.github.io/react/docs/context.html – Matthew Herbst 14 September 2016 в 00:19
  • 3
    Опоры! = Контекст – Petr Peller 11 December 2016 в 20:14
  • 4
    Я отредактировал свой ответ, чтобы решить ваши проблемы. Спасибо, что сделал это лучше. – lustoykov 6 January 2017 в 18:44
  • 5
    Может быть, я не понимаю, но разве нехорошо говорить, что «контекст делает реквизит доступным»? Когда я последний раз использовал контекст, это была отдельная вещь (т. Е. this.context) - она ​​не волшебным образом объединяла контекст с реквизитами. Вы должны были преднамеренно установить и использовать контекст, который является совершенно другим. – Josh 21 April 2017 в 12:37

Parent.jsx:

import React from 'react';

const doSomething = value => {};

const Parent = props => (
  <div>
    {
      !props || !props.children 
        ? <div>Loading... (required at least one child)</div>
        : !props.children.length 
            ? <props.children.type {...props.children.props} doSomething={doSomething} {...props}>{props.children}</props.children.type>
            : props.children.map((child, key) => 
              React.cloneElement(child, {...props, key, doSomething}))
    }
  </div>
);

Child.jsx:

import React from 'react';

/* but better import doSomething right here,
   or use some flux store (for example redux library) */
export default ({ doSomething, value }) => (
  <div onClick={() => doSomething(value)}/>
);

и main.jsx:

import React from 'react';
import { render } from 'react-dom';
import Parent from './Parent';
import Child from './Child';

render(
  <Parent>
    <Child/>
    <Child value='1'/>
    <Child value='2'/>
  </Parent>,
  document.getElementById('...')
);

см. пример здесь: https://plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview

1
ответ дан Maksim Kostromin 16 August 2018 в 00:57
поделиться

Если у вас есть несколько детей, которых вы хотите передать реквизиты , вы можете сделать это таким образом, используя React.Children.map:

render() {
    let updatedChildren = React.Children.map(this.props.children,
        (child) => {
            return React.cloneElement(child, { newProp: newProp });
        });

    return (
        <div>
            { updatedChildren }
        </div>
    );
}

Если ваш компонент имеет только одного ребенка, нет необходимости в сопоставлении, вы можете просто клонировать элемент сразу:

render() {
    return (
        <div>
            {
                React.cloneElement(this.props.children, {
                    newProp: newProp
                })
            }
        </div>
    );
}
2
ответ дан Nesha Zoric 16 August 2018 в 00:57
поделиться

Самый быстрый способ сделать это:

    {React.cloneElement(this.props.children, this.props)}
0
ответ дан nitte93user3232918 16 August 2018 в 00:57
поделиться

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

var Parent = React.createClass({
doSomething: function() {
    console.log('doSomething!');
},

render: function() {
    var that = this;
    var childrenWithProps = React.Children.map(this.props.children, function(child) {
        return React.cloneElement(child, { doSomething: that.doSomething });
    });

    return <div>{childrenWithProps}</div>
}})

Обновление: это исправление для ECMAScript 5, в ES6 нет необходимости в var, что = this

5
ответ дан olenak 16 August 2018 в 00:57
поделиться
  • 1
    или просто используйте bind() – plus- 4 January 2016 в 11:17
  • 2
    или использовать функцию стрелки, которая связывается с лексической областью, я обновил свой ответ – Dominic Tobias 22 January 2016 в 10:39
  • 3
    что, если doSomething взял объект, например doSomething: function(obj) { console.log(obj) }, а в Ребенке вы вызывали this.props.doSomething(obj) для выхода из "obj" – conor909 10 April 2016 в 09:39
  • 4
    @ plus- я знаю, что это старо, но использование bind здесь - ужасная идея, bind создает новую функцию, которая связывает контекст с новым. в основном функцию, вызывающую метод apply. использование функции bind() в функции рендеринга будет создавать новую функцию каждый раз при вызове метода визуализации. – Bamieh 20 November 2016 в 14:38

Более чистый способ рассмотрения одного или нескольких детей

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
5
ответ дан pkoch 16 August 2018 в 00:57
поделиться
  • 1
    Это не работает для меня, это дает ошибку: дети не определены. – Deelux 27 November 2016 в 10:05
  • 2
    @Deelux this.props.children вместо детей – Martin Dawson 15 December 2016 в 21:05
  • 3
    это передает ребенка своим детям в this.props. В общем, я бы рекомендовал клонирование только с конкретными реквизитами, а не с целым шебангом. – Andy 2 November 2017 в 07:21
  • 4
    Передача {...this.props} не сработала для меня, правильно ли {...child.props}? – Felipe Augusto 6 July 2018 в 07:11

Ни один из ответов не касается вопроса о том, что у детей есть НЕ РЕАКТИВНЫЕ компоненты, такие как текстовые строки. Обходной путь может быть примерно таким:

// Render method of Parent component
render(){
    let props = {
        setAlert : () => {alert("It works")}
    };
    let childrenWithProps = React.Children.map( this.props.children, function(child) {
        if (React.isValidElement(child)){
            return React.cloneElement(child, props);
        }
          return child;
      });
    return <div>{childrenWithProps}</div>

}
3
ответ дан poynting 16 August 2018 в 00:57
поделиться

Это можно сделать просто как:

interface PaginationItemProps extends React.HTMLProps<HTMLDivElement> {
}

const PaginationItem = (props: PaginationItemProps) => {
   return <div>{props.children}</div>;
}

class Pagination extends React.Component<PaginationProps> {
  render() {
    return <div>          
        <PaginationItem>CHILDREN RENDER</PaginationItem>
    </div>
 }
}
-1
ответ дан Shafqat Ali 16 August 2018 в 00:57
поделиться

Согласно документации cloneElement()

React.cloneElement(
  element,
  [props],
  [...children]
)

Clone и возвратите новый элемент React, используя элемент в качестве отправной точки. Получившийся элемент будет иметь реквизит оригинального элемента с новым реквизитом, сложенным неглубоко. Новые дети заменят существующих детей. ключ и ref из исходного элемента будут сохранены.

React.cloneElement() почти эквивалентен:

<element.type {...element.props} {...props}>{children}</element.type>

Однако он также сохраняет ссылки. Это означает, что если вы получите ребенка с рефлексией на нем, вы случайно не украдете его у своего предка. Вы получите тот же ref, что и ваш новый элемент.

Итак, cloneElement - это то, что вы использовали бы для предоставления пользовательских реквизитов для детей. Однако в компоненте может быть несколько дочерних элементов, и вам нужно будет зациклиться на нем. Другие варианты подсказок позволяют вам сопоставить их с помощью React.Children.map. Однако React.Children.map, в отличие от React.cloneElement, изменяет ключи добавления Element и дополнительно .$ в качестве префикса. Проверьте этот вопрос для получения дополнительной информации: React.cloneElement внутри React.Children.map вызывает изменение ключей элемента

Если вы хотите этого избежать, вместо этого вы должны пойти на forEach, например

render() {
    const newElements = [];
    React.Children.forEach(this.props.children, 
              child => newElements.push(
                 React.cloneElement(
                   child, 
                   {...this.props, ...customProps}
                )
              )
    )
    return (
        <div>{newElements}</div>
    )

}
0
ответ дан Shubham Khatri 16 August 2018 в 00:57
поделиться

В ответ на @and_rest ответьте, как я клонировал детей и добавляю класс.

<div className="parent">
    {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
1
ответ дан sidonaldson 16 August 2018 в 00:57
поделиться

Вам больше не нужно {this.props.children}. Теперь вы можете обернуть ваш дочерний компонент с помощью render в Route и передать свои реквизиты, как обычно:

<BrowserRouter>
  <div>
    <ul>
      <li><Link to="/">Home</Link></li>
      <li><Link to="/posts">Posts</Link></li>
      <li><Link to="/about">About</Link></li>
    </ul>

    <hr/>

    <Route path="/" exact component={Home} />
    <Route path="/posts" render={() => (
      <Posts
        value1={1}
        value2={2}
        data={this.state.data}
      />
    )} />
    <Route path="/about" component={About} />
  </div>
</BrowserRouter>
4
ответ дан yeasayer 16 August 2018 в 00:57
поделиться
  • 1
    Рендеринг реквизитов теперь является стандартным для React ( reactjs.org/docs/render-props.html ) и заслуживает рассмотрения в качестве нового принятого ответа на этот вопрос. – Ian Danforth 18 April 2018 в 16:39
  • 2
    Как это ответ на вопрос? – Maximo Dominguez 15 May 2018 в 21:55
Другие вопросы по тегам:

Похожие вопросы: