14

Как передать пропсы в {this.props.children}

13

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

<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 не определен
  },
  render: function() {
    return (<div onClick={this.onClick}></div>);
  }
});

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

5 ответ(ов)

5

Для немного более аккуратного решения попробуйте:

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

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

<div>
    {React.cloneElement(this.props.children[0], { loggedIn: true, testPropB: true })}
    {React.cloneElement(this.props.children[1], { loggedIn: true, testPropA: false })}
</div>
1

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

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

У меня это сработало с версией react-15.1.

Использование {...this.props} рекомендуется в документации React.

0

Передача Props в Ненастоящие Дочерние Компоненты

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

import * as React from 'react';

// React.createContext принимает defaultValue в качестве первого параметра
const MyContext = React.createContext(); 

function Parent(props) {
  const doSomething = React.useCallback((childName) => {
    // Выполните здесь что-то с переданным значением
  }, []);

  return (
     <MyContext.Provider value={{ doSomething }}>
       {props.children}
     </MyContext.Provider>
  );
}
 
function Child(props) {
  const myContext = React.useContext(MyContext);

  const onClick = React.useCallback(() => {
    myContext.doSomething(props.childName);
  }, [props.childName, myContext.doSomething]);

  return (
    <div onClick={onClick}>{props.childName}</div>
  );
}

// Пример использования Parent и Child

import * as React from 'react';

function SomeComponent() {
  return (
    <Parent>
      <Child childName="Billy" />
      <Child childName="Bob" />
    </Parent>
  );
}

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

function SomeComponent() {
  return (
    <Parent>
      <Child childName="Billy" />
      <SomeOtherComp>
        <Child childName="Bob" />
      </SomeOtherComp>
    </Parent>
  );
}

Таким образом, использование контекста позволяет более элегантно и удобно передавать данные через многоуровневую структуру компонентов в React.

0

Наилучший способ передачи свойств в компонентах React — это использование паттерна "функция как дочерний элемент" (Function as Child Component). Этот подход позволяет вам динамически передавать свойства от родительского компонента к дочернему.

Вот пример, который иллюстрирует данный подход:

const Parent = ({ children }) => {
    const somePropsHere = {
      style: {
        color: "red"
      }
      // любые другие свойства...
    }
    return children(somePropsHere)
}

const ChildComponent = props => <h1 {...props}>Hello world!</h1>

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

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

0

Ответ на вопрос о том, почему нужно использовать that вместо this внутри функции, определённой в методе map, можно сформулировать следующим образом:

В приведённом вами коде, когда вы используете обычную функцию в качестве второго аргумента метода map, контекст this внутри этой функции будет указывать на глобальный объект (или undefined в строгом режиме), а не на экземпляр родительского компонента. Поэтому метод doSomething, который вы пытаетесь вызвать, не будет доступен, поскольку this не указывает на нужный объект.

Чтобы исправить это, вы можете сохранить ссылку на this в переменную that перед использованием метода map, как показано в вашем примере. Это позволяет избежать потери контекста:

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

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

class Parent extends React.Component {
    doSomething() {
        console.log('doSomething!');
    }

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

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

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь