Как передать реквизит {this.props.children}

889

Я пытаюсь найти правильный способ определить некоторые компоненты, которые могут быть использованы в общем виде:

<Parent>
  <Child value="1">
  <Child value="2">
</Parent>

Существует логика для рендеринга между родительскими и дочерними компонентами, конечно, вы можете себе представить <select>и <option>в качестве примера этой логики.

Это фиктивная реализация для цели вопроса:

var Parent = React.createClass({
  doSomething: function(value) {
  },
  render: function() {
    return (<div>{this.props.children}</div>);
  }
});

var Child = React.createClass({
  onClick: function() {
    this.props.doSomething(this.props.value); // doSomething is undefined
  },
  render: function() {
    return (<div onClick={this.onClick}></div>);
  }
});

Вопрос в том, когда вы используете {this.props.children}для определения компонента-оболочки, как вы передаете какое-то свойство всем его дочерним элементам?

Плюс
источник

Ответы:

955

Клонирование детей с новыми реквизитами

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

import React, { Children, isValidElement, cloneElement } from 'react';

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

function Parent({ children }) {
  function doSomething(value) {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    const childrenWithProps = Children.map(children, child => {
      // Checking isValidElement is the safe way and avoids a TS error too.
      if (isValidElement(child)) {
        return cloneElement(child, { doSomething })
      }

      return child;
    });

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

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

Скрипка: https://jsfiddle.net/2q294y43/2/

Вызов детей как функция

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

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

function Parent({ children }) {
  function doSomething(value) {
    console.log('doSomething called by child with value:', value);
  }

  render() {
    // Note that children is called as a function and we can pass args to it
    return <div>{children(doSomething)}</div>
  }
};

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

Вместо <React.Fragment>или просто <>вы также можете вернуть массив, если хотите.

Скрипка: https://jsfiddle.net/ferahl/y5pcua68/7/

Dominic
источник
7
Это не работает для меня. это не определено в React.cloneElement ()
Патрик
12
Этот ответ не работает, valueпереданный doSomethingтеряется.
Дэйв
3
@DominicTobias Arg, извините, я переключил console.log для предупреждения и забыл объединить два параметра в одну строку.
Дейв
1
Этот ответ был очень полезен, но я столкнулся с проблемой, которая здесь не упоминается, и мне было интересно, изменилась ли какая-то новая вещь или что-то странное с моей стороны. Когда я клонировал свой дочерний элемент, его дочерний элемент был установлен на старый элемент, пока я не добавил this.props.children.props.children в третий аргумент cloneElement.
афенин
7
Что делать, если дочерний элемент загружается через маршрут (v4), который загружается с отдельной страницы маршрута?
болтать
394

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

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

Редактировать: Вы можете использовать с несколькими отдельными дочерними элементами (дочерний элемент должен быть компонентом). Протестировано в 16.8.6

<div>
    {React.cloneElement(props.children[0], { loggedIn: true, testingTwo: true })}
    {React.cloneElement(props.children[1], { loggedIn: true, testProp: false })}
</div>
Андрес Ф Гарсия
источник
10
Я использовал самый рейтинговый ответ, но этот гораздо проще! Это решение также используется на странице примеров реакции-маршрутизатора.
captDaylight
10
Может ли кто-нибудь объяснить, как это работает (или что на самом деле делает)? Читая документы , я не мог понять, как это сойдет на детей и добавит эту опору каждому ребенку - это то, для чего он предназначен? Если это произойдет, как мы узнаем, что это то, что он будет делать? Совершенно не очевидно, что даже допустимо передавать непрозрачную структуру данных ( this.props.children) cloneElement..., которая ожидает ... элемент.
GreenAsJade
51
Точно, это, кажется, не работает больше чем с одним ребенком.
Данита
17
Таким образом, вы можете написать код, который работает, когда кто-то передает только один дочерний элемент в компонент, но когда он добавляет другого, он выходит из строя ... это не звучит здорово по номинальной стоимости? Казалось бы, это ловушка для ОП, который специально спросил о передаче реквизита всем детям.
GreenAsJade
10
@GreenAsJade это хорошо, пока ваш компонент ожидает одного ребенка. Вы можете определить через ваши компоненты propTypes, что он ожидает одного дочернего элемента. React.Children.onlyфункция возвращает единственного потомка или выдает исключение, если их несколько (этого не было бы, если бы не было варианта использования).
cchamberlain
80

Попробуй это

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

У меня это сработало с использованием response-15.1.

7puns
источник
3
Можно ли вернуть React.cloneElement()напрямую, не окружая его <div>тегами? Что, если дочерний элемент <span>(или что-то еще) и мы хотим сохранить его тип элемента тега?
adrianmc
1
Если это один ребенок, вы можете оставить упаковку, и это решение работает только для одного ребенка, так что да.
ThaJay
1
Работает для меня. Без вложенного <div> это нормально.
Crash
4
Если вам необходимо явно указать, что вы получаете только одного дочернего элемента, вы можете сделать это, React.cloneElement(React.Children.only(this.props.children), {...this.props})что приведет к ошибке, если будет передано более одного дочернего элемента. Тогда вам не нужно заключать в div.
Анандерсон
1
Этот ответ может привести к значению TypeError: циклический объект. Если вы не хотите, чтобы один из предметов ребенка был самим собой, используйте let {children, ...acyclicalProps} = this.propsи потом React.cloneElement(React.Children.only(children), acyclicalProps).
Parabolord
69

Передайте реквизит, чтобы направить детей.

Посмотреть все остальные ответы

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

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

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

Он основан на принципе Потребитель / Обеспечить. Сначала создайте свой контекст

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

Любомир
источник
6
В отличие от принятого ответа, он будет работать правильно, даже если есть другие элементы, включенные в Parent. Это определенно лучший ответ.
Zaptree
6
Реквизит! = Контекст
Петр Пеллер
Вы не можете зависеть от изменений, распространяющихся через контекст. Используйте реквизит, когда это возможно, они меняются.
ThaJay
1
Может быть, я не понимаю, но правильно ли сказать, что «контекст делает реквизит доступным»? Когда я в последний раз использовал контекст, это была отдельная вещь (то есть this.context) - он волшебным образом не сливал контекст с реквизитом. Вы должны были намеренно установить и использовать контекст, а это совсем другое.
Джош
Вы прекрасно понимаете, это было неправильно. Я отредактировал свой ответ.
Любомир
48

Передача реквизита вложенным детям

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

import * as React from 'react';

// React.createContext accepts a defaultValue as the first param
const MyContext = React.createContext(); 

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

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

class Child extends React.Component {
  static contextType = MyContext;

  onClick = () => {
    this.context.doSomething(this.props.value);
  };      

  render() {
    return (
      <div onClick={this.onClick}>{this.props.value}</div>
    );
  }
}


// Example of using Parent and Child

import * as React from 'react';

class SomeComponent extends React.Component {

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

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

class SomeComponent extends React.Component {

  render() {
    return (
      <Parent>
        <Child value={1} />
        <SomeOtherComp><Child value={2} /></SomeOtherComp>
      </Parent>
    );
  }
}
Кеннет Труонг
источник
3
Можете ли вы объяснить, почему => функции являются плохой практикой? Функция => помогает связать обработчики событий для получения thisконтекста
Кеннет Труонг
@KennethTruong, потому что каждый раз, когда он рендерит, он создает функцию.
itdoesntwork
9
@itdoesntwork это не правда. Он создает новую функцию только при создании класса. Это не создается во время функции рендеринга ..
Кеннет Труонг
@KennethTruong reactjs.org/docs/faq-functions.html#arrow-function-in-render Я думал, что вы говорите о функции стрелки в рендере.
itdoesntwork
24

Вы можете использовать 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, он также сохраняет ссылки. Это означает, что если вы получите ребенка с опорой на него, вы не сможете случайно украсть его у своего предка. Вы получите ту же ссылку, прикрепленную к вашему новому элементу.

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

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

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

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

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

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

Алиреза
источник
19

Лучший способ, который позволяет вам передавать свойства, это childrenкак функция

Пример:

export const GrantParent = () => {
  return (
    <Parent>
      {props => (
        <ChildComponent {...props}>
          Bla-bla-bla
        </ChildComponent>
      )}
    </Parent>
  )
}

export const Parent = ({ children }) => {
    const somePropsHere = { //...any }
    <>
        {children(somePropsHere)}
    </>
}
Ник овчинников
источник
1
Это кажется мне более простым (и лучшим по производительности?), Чем принятый ответ.
Шикио
2
Это требует, чтобы дети были функцией, а не работало с вложенными компонентами
цифровая иллюзия
@digitalillusion, я не понимаю, что это значит nested components. У React нет вложенных шаблонов, только композиции. Да, дети должны быть функцией, никаких конфликтов нет, потому что это действительный JSX-ребенок. Можете ли вы привести пример с nesting components?
Ник Овчинников
1
Вы правы, что дело с глубоко вложенными детьми может быть рассмотрено <Parent>{props => <Nest><ChildComponent /></Nest>}</Parent>вместо (не работает), <Parent><Nest>{props => <ChildComponent />}</Nest></Parent>поэтому я согласен, что это лучший ответ
цифровая иллюзия
При попытке получить следующее:TypeError: children is not a function
Райан Прентисс
6

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

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 that = this

olenak
источник
13
или просто используйтеbind()
плюс-
1
или используйте функцию стрелки, которая привязывается к лексической области, я обновил свой ответ
Доминик,
что если doSomethingdoSomething: function(obj) { console.log(obj) }this.props.doSomething(obj)"obj"
взять
4
@ плюс - я знаю, что это старо, но использование связывания здесь - ужасная идея, связывание создает новую функцию, которая связывает контекст с новым. в основном функция, вызывающая applyметод. использование bind()в функции рендеринга будет создавать новую функцию каждый раз, когда вызывается метод рендеринга.
Bamieh
6

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

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>
and_rest
источник
Этот не работает для меня, он дает ошибку: дети не определены.
Deelux
@Deelux this.props.children вместо детей
Мартин Доусон
это передает ребенка как своих собственных детей this.props. В общем, я бы рекомендовал клонировать только с определенными реквизитами, а не со всем.
Энди
Передача {...this.props}не работает для меня, {...child.props}правильный путь ?
Фелипе Аугусто
Для функциональных компонентов:React.Children.map(children, child => React.cloneElement(child, props))
vsync
5

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

// 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>

}
Пойнтинга
источник
5

Вам больше не нужно {this.props.children}. Теперь вы можете обернуть свой дочерний компонент с помощью renderin 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>
Yeasayer
источник
2
Реквизит рендеринга теперь является стандартным в React ( reactjs.org/docs/render-props.html ) и заслуживает рассмотрения в качестве нового принятого ответа на этот вопрос.
Ян Данфорт
19
Как это ответ на вопрос?
Максимо Домингес
4

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

Максим Костромин
источник
4

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

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

Александр Чередниченко
источник
Это не анти-паттерн: youtube.com/watch?v=BcVAq3YFiuc
Пьетро Коэльо
4

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

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

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

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

render() {
    return (
        <div>
            {
                React.cloneElement(this.props.children, {
                    newProp: newProp
                })
            }
        </div>
    );
}
Неша Зорич
источник
3

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

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

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

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

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

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

Таким образом, cloneElement - это то, что вы бы использовали для предоставления пользовательских реквизитов детям. Тем не менее, в компоненте может быть несколько дочерних элементов, и вам нужно будет зациклить его. Что другие ответы предлагают для вас, чтобы отобразить их, используя React.Children.map. Однако в React.Children.mapотличие от React.cloneElementизменений ключи элемента добавляются и добавляются .$в качестве префикса. Проверьте этот вопрос для более подробной информации: 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>
    )

}
Шубхам Хатри
источник
2

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

<div className="parent">
    {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>
sidonaldson
источник
2

Я думаю, что опора рендеринга является подходящим способом справиться с этим сценарием

Вы позволяете Родителю предоставлять необходимые реквизиты, используемые в дочернем компоненте, путем рефакторинга Родительского кода, чтобы выглядеть примерно так:

const Parent = ({children}) => {
  const doSomething(value) => {}

  return children({ doSomething })
}

Затем в дочернем компоненте вы можете получить доступ к функции, предоставленной родителем следующим образом:

class Child extends React {

  onClick() => { this.props.doSomething }

  render() { 
    return (<div onClick={this.onClick}></div>);
  }

}

Теперь структура fianl будет выглядеть так:

<Parent>
  {(doSomething) =>
   (<Fragment>
     <Child value="1" doSomething={doSomething}>
     <Child value="2" doSomething={doSomething}>
    <Fragment />
   )}
</Parent>
ZEE
источник
Что если родитель - это просто оболочка для {children}, которое является текстовой областью в другом компоненте класса?
Адебайо
2

Способ 1 - клонирование детей

const Parent = (props) => {
   const attributeToAddOrReplace= "Some Value"
   const childrenWithAdjustedProps = React.Children.map(props.children, child =>
      React.cloneElement(child, { attributeToAddOrReplace})
   );

   return <div>{childrenWithAdjustedProps }</div>
}

Способ 2 - использовать составной контекст

Контекст позволяет вам передавать реквизит глубокому дочернему компоненту без явной передачи его в качестве реквизита между компонентами между ними.

Контекст имеет недостатки:

  1. Данные не текут обычным способом - через реквизит.
  2. Использование контекста создает договор между потребителем и поставщиком. Может быть сложнее понять и воспроизвести требования, необходимые для повторного использования компонента.

Использование компонуемого контекста

export const Context = createContext<any>(null);

export const ComposableContext = ({ children, ...otherProps }:{children:ReactNode, [x:string]:any}) => {
    const context = useContext(Context)
    return(
      <Context.Provider {...context} value={{...context, ...otherProps}}>{children}</Context.Provider>
    );
}

function App() {
  return (
      <Provider1>
            <Provider2> 
                <Displayer />
            </Provider2>
      </Provider1>
  );
}

const Provider1 =({children}:{children:ReactNode}) => (
    <ComposableContext greeting="Hello">{children}</ComposableContext>
)

const Provider2 =({children}:{children:ReactNode}) => (
    <ComposableContext name="world">{children}</ComposableContext>
)

const Displayer = () => {
  const context = useContext(Context);
  return <div>{context.greeting}, {context.name}</div>;
};
Бен Карп
источник
Немного поздно, но не могли бы вы объяснить обозначения в {children}:{children:ReactNode}?
Камилла
@camille, это вещь Typescript. Глядя на это сейчас, я бы просто ответил с Javascript, и даже если бы я написал Typescript, я бы сделал это по-другому. Могу отредактировать его в будущем.
Бен Карп
1
@camille, в основном это означает, что значение, которое имеет ключ, "children"имеет типReactNode
Бен Карп
1

Самый лучший способ сделать это:

    {React.cloneElement(this.props.children, this.props)}
nitte93user3232918
источник
5
Разве это не копирует this.props.children в this.props.children ребенка? и в действительности копировать ребенка в себя?
Аршабх Агарвал
1

Любой, у кого есть единственный дочерний элемент, должен это сделать.

{React.isValidElement(this.props.children)
                  ? React.cloneElement(this.props.children, {
                      ...prop_you_want_to_pass
                    })
                  : null}
user10884362
источник
0

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

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>
  }
})
amit_183
источник
4
Нет, я не хочу ограничивать содержимое моей обертки каким-то конкретным содержимым.
плюс-
0

Почему-то React.children не работал для меня. Это то, что сработало для меня.

Я хотел просто добавить класс для ребенка. похоже на изменение реквизита

 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 . Вы можете передать любую опору подобным образом

aWebDeveloper
источник
0

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

class Child extends React.Component {
  render() {
    return <div className="Child">
      Child
      <p onClick={this.props.doSomething}>Click me</p>
           {this.props.a}
    </div>;
  }
}

class Parent extends React.Component {
  doSomething(){
   alert("Parent talks"); 
  }

  render() {
    return <div className="Parent">
      Parent
      {this.props.render({
        anythingToPassChildren:1, 
        doSomething: this.doSomething})}
    </div>;
  }
}

class Application extends React.Component {
  render() {
    return <div>
      <Parent render={
          props => <Child {...props} />
        }/>
    </div>;
  }
}

Пример на codepen

omeralper
источник
0

При использовании функциональных компонентов часто TypeError: Cannot add property myNewProp, object is not extensibleвозникает ошибка при попытке установить новые свойства props.children. Для этого есть обходной путь: клонирование реквизита, а затем клонирование самого ребенка с помощью нового реквизита.

const MyParentComponent = (props) => {
  return (
    <div className='whatever'>
      {props.children.map((child) => {
        const newProps = { ...child.props }
        // set new props here on newProps
        newProps.myNewProp = 'something'
        const preparedChild = { ...child, props: newProps }
        return preparedChild
      })}
    </div>
  )
}
хозяин
источник