Render Props (рендер-пропсы) — это продвинутый паттерн в React, при котором компонент получает функцию через пропс (обычно render
или children
), определяющую что рендерить, и вызывает её, передавая необходимые данные.
<DataProvider
render={(data) => <DisplayComponent data={data} />}
/>
<DataProvider>
{(data) => <DisplayComponent data={data} />}
</DataProvider>
class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
<div onMouseMove={this.handleMouseMove}>
{/* Вызываем функцию и передаём текущее состояние */}
{this.props.children(this.state)}
</div>
);
}
}
<MouseTracker>
{({ x, y }) => (
<div>
<h1>Позиция курсора</h1>
<p>X: {x}, Y: {y}</p>
</div>
)}
</MouseTracker>
class DataFetcher extends React.Component {
state = { data: null, loading: true, error: null };
async componentDidMount() {
try {
const response = await fetch(this.props.url);
const data = await response.json();
this.setState({ data, loading: false });
} catch (error) {
this.setState({ error, loading: false });
}
}
render() {
return this.props.children(this.state);
}
}
// Использование
<DataFetcher url="/api/user">
{({ data, loading, error }) => {
if (loading) return <Spinner />;
if (error) return <Error message={error.message} />;
return <UserProfile data={data} />;
}}
</DataFetcher>
class Toggle extends React.Component {
state = { on: false };
toggle = () => this.setState(prev => ({ on: !prev.on }));
render() {
return this.props.children({
on: this.state.on,
toggle: this.toggle
});
}
}
// Использование
<Toggle>
{({ on, toggle }) => (
<button onClick={toggle}>
{on ? 'Включено' : 'Выключено'}
</button>
)}
</Toggle>
<ThemeProvider>
{theme => (
<LocaleProvider>
{locale => (
<Page theme={theme} locale={locale} />
)}
</LocaleProvider>
)}
</ThemeProvider>
// Мемоизация рендер-функции
class OptimizedComponent extends React.Component {
renderContent = (data) => {
return <ExpensiveComponent data={data} />;
};
render() {
return (
<DataProvider>
{this.renderContent}
</DataProvider>
);
}
}
<Mouse>
{({ x, y }) => <Cursor x={x} y={y} />}
</Mouse>
children
как функцию для основного случаяfunction useMousePosition() {
const [position, setPosition] = useState({ x: 0, y: 0 });
// Логика отслеживания мыши...
return position;
}
// Использование
const { x, y } = useMousePosition();
const withMousePosition = (Component) => {
return class extends React.Component {
// Логика отслеживания мыши...
render() {
return <Component {...this.props} mouse={this.state} />;
}
};
};
render
children
как функциюПример полной реализации:
// Компонент с Render Prop
class ScrollPosition extends React.Component {
state = { scrollY: 0 };
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
this.setState({ scrollY: window.scrollY });
};
render() {
return this.props.children(this.state);
}
}
// Использование
<ScrollPosition>
{({ scrollY }) => (
<header className={scrollY > 100 ? 'scrolled' : ''}>
Позиция скролла: {scrollY}px
</header>
)}
</ScrollPosition>