Как передать пропсы в {this.props.children}
Я пытаюсь найти правильный способ определения компонентов, которые можно было бы использовать универсально:
<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 ответ(ов)
Для немного более аккуратного решения попробуйте:
<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>
Попробуйте это:
<div>{React.cloneElement(this.props.children, {...this.props})}</div>
У меня это сработало с версией react-15.1.
Использование {...this.props}
рекомендуется в документации React.
Передача 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.
Наилучший способ передачи свойств в компонентах 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
. Это позволяет не только передавать стили, но и любые другие свойства, что делает ваш код более гибким и переиспользуемым. Такой подход особенно полезен, когда нужно передавать данные из родительского компонента дочерним компонентам без необходимости особого взаимодействия через контекст или пропсы на более глубоком уровне.
Ответ на вопрос о том, почему нужно использовать 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 использование стрелочных функций позволяет избежать необходимости сохранять контекст явно.
Как программно выполнять навигацию с помощью React Router?
Как условно добавлять атрибуты к компонентам React?
Как установить фокус на поле ввода после рендеринга?
Метод set в useState не отражает изменения немедленно
В чем разница между React Native и React?