在 render() 方法中不使用 React.cloneElement 传递道具
Props are not passed with React.cloneElement in the render() method
我有两个 React 组件,一个 Layout
class 和一个 HomePage
class:
HomePage
是一个需要有 products
属性的组件。
HomePage.js
import React, { Component } from 'react';
export class HomePage extends Component {
render() {
if (!this.props.products) {
return (<div>Products not loaded yet</div>);
}
return (<div>Products loaded!</div>);
}
}
Layout
是一个组件,显示 children 来自 react-router
建立的路线。
此 class 负责使用 React.cloneElement
将 products
道具传递给 children
Layout.js
import React, { Component } from 'react';
import { NavMenu } from './NavMenu';
import { Footer } from './Footer';
export class Layout extends Component {
constructor(props) {
super(props);
this.state = {
products: null,
loading: true
};
}
// Make an api call when the component is mounted in order to pass
// additional props to the children
componentDidMount() {
this.populateProductsData();
}
async populateProductsData() {
const response = await fetch('api/products/all');
const data = await response.json();
this.setState({ products: data, loading: false });
}
render() {
if (this.state.loading) {
return (<div>App loading</div>);
}
const childrenWithProps = React.Children.map(this.props.children, child => {
const props = { products: this.state.products };
if (React.isValidElement(child)) {
return React.cloneElement(child, props);
}
return child;
});
return (
<div>
<NavMenu />
{childrenWithProps}
<Footer />
</div>
);
}
}
路由是在 App
组件中进行的:
App.js
export default class App extends Component {
render () {
return (
<Layout>
<Route exact path='/'
component={HomePage}/>
</Layout>
);
}
因此,我期待
- 在未进行 API 呼叫时有一个包含
App loading
消息的页面
- 有一个包含
Products not loaded yet
消息的页面,而道具尚未传递给 Layout
children
- 有一个包含
Products loaded!
消息的页面
但是,应用程序卡在第二步:children 组件从未接收到 products
属性。代码编译,没有运行时错误,触发 back-end Api 并发送有效响应。
为什么 product
道具在 child HomePage
组件的 render()
方法中永远不可用?
编辑:
根据@Nikita Chayka 的回答,props 应该在路由中传递:
Layout.js
export class Layout extends Component {
render() {
return (
<div>
<NavMenu />
{this.props.children}
<Footer />
</div>
);
}
}
App.js
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
products: null,
loading: true
};
}
componentDidMount() {
this.populateProductsData();
}
async populateProductsData() {
const response = await fetch('/api/products/all');
const data = await response.json();
this.setState({ products: data, loading: false });
}
render() {
if (this.state.loading)
return (<div>App loading</div>);
return (
<Layout>
<Route exact path='/'
render={(props) => (<HomePage {...props} products={this.state.products}/>)}/>
</Layout>
);
}
}
你的 Layout 组件会将 products prop 传递给 Route 组件,而不是 Home 组件,基本上你会有
<Route products={} component={Home} path="/" exact/>
但是你需要把它传递给主页,你可以在这里检查想法 - https://ui.dev/react-router-v4-pass-props-to-components/
编辑
你不应该提供组件 属性 给路由,只渲染。
我有两个 React 组件,一个 Layout
class 和一个 HomePage
class:
HomePage
是一个需要有 products
属性的组件。
HomePage.js
import React, { Component } from 'react';
export class HomePage extends Component {
render() {
if (!this.props.products) {
return (<div>Products not loaded yet</div>);
}
return (<div>Products loaded!</div>);
}
}
Layout
是一个组件,显示 children 来自 react-router
建立的路线。
此 class 负责使用 React.cloneElement
products
道具传递给 children
Layout.js
import React, { Component } from 'react';
import { NavMenu } from './NavMenu';
import { Footer } from './Footer';
export class Layout extends Component {
constructor(props) {
super(props);
this.state = {
products: null,
loading: true
};
}
// Make an api call when the component is mounted in order to pass
// additional props to the children
componentDidMount() {
this.populateProductsData();
}
async populateProductsData() {
const response = await fetch('api/products/all');
const data = await response.json();
this.setState({ products: data, loading: false });
}
render() {
if (this.state.loading) {
return (<div>App loading</div>);
}
const childrenWithProps = React.Children.map(this.props.children, child => {
const props = { products: this.state.products };
if (React.isValidElement(child)) {
return React.cloneElement(child, props);
}
return child;
});
return (
<div>
<NavMenu />
{childrenWithProps}
<Footer />
</div>
);
}
}
路由是在 App
组件中进行的:
App.js
export default class App extends Component {
render () {
return (
<Layout>
<Route exact path='/'
component={HomePage}/>
</Layout>
);
}
因此,我期待
- 在未进行 API 呼叫时有一个包含
App loading
消息的页面 - 有一个包含
Products not loaded yet
消息的页面,而道具尚未传递给Layout
children - 有一个包含
Products loaded!
消息的页面
但是,应用程序卡在第二步:children 组件从未接收到 products
属性。代码编译,没有运行时错误,触发 back-end Api 并发送有效响应。
为什么 product
道具在 child HomePage
组件的 render()
方法中永远不可用?
编辑:
根据@Nikita Chayka 的回答,props 应该在路由中传递:
Layout.js
export class Layout extends Component {
render() {
return (
<div>
<NavMenu />
{this.props.children}
<Footer />
</div>
);
}
}
App.js
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
products: null,
loading: true
};
}
componentDidMount() {
this.populateProductsData();
}
async populateProductsData() {
const response = await fetch('/api/products/all');
const data = await response.json();
this.setState({ products: data, loading: false });
}
render() {
if (this.state.loading)
return (<div>App loading</div>);
return (
<Layout>
<Route exact path='/'
render={(props) => (<HomePage {...props} products={this.state.products}/>)}/>
</Layout>
);
}
}
你的 Layout 组件会将 products prop 传递给 Route 组件,而不是 Home 组件,基本上你会有
<Route products={} component={Home} path="/" exact/>
但是你需要把它传递给主页,你可以在这里检查想法 - https://ui.dev/react-router-v4-pass-props-to-components/
编辑
你不应该提供组件 属性 给路由,只渲染。