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

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

<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} чтобы определить компонент оболочки, как вы передаете вниз какая-то собственность для всех его детей?

18 ответов


можно использовать реагировать.Дети!--3--> для итерации над детьми, а затем клонировать каждый элемент с новыми реквизитами (мелкий объединены) с помощью реагировать.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')
);

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


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

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

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


попробуй такое

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

это сработало для меня, используя react-15.1.


Pass реквизит для прямых детей.

см. все остальные ответы

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

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

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

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

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

затем используйте via

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

и

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

все потребители, которые являются потомками поставщика будет повторно визуализировать всякий раз, когда значение prop поставщика изменяется. распространение от поставщика к его потомкам-потребителям не подлежит методу shouldComponentUpdate, поэтому потребитель обновляется, даже если компонент ancestor выходит из обновления. 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>
    );
  }
}

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


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

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

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

в React v0.13 RC2 мы представим новый API, подобный Реагировать.дополнения.cloneWithProps, с этим подпись:

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

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

реагировать.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 })
});

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

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

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

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


С обновлением до реагировать 16.3 теперь вы можете использовать реагировать.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>
  }
}

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

class SomeComponent extends React.Component {

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

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

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

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


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

<div>
   { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))}
</div>

вам больше не нужно {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>

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

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

}

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

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

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

<div className="parent">
    {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))}
</div>

родитель.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>
);

ребенок.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


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

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

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

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

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

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

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

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


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

функция как дочерние компоненты


это то, что вам требуется?

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

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

    {React.cloneElement(this.props.children, this.props)}

какая-то причина реагировать.дети на меня не работали. Это то, что сработало для меня.

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

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

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

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