React Meteor 似乎无法传递道具
React Meteor cannot seem to pass props
所以我一直在尝试自学 react 和 meteor 来创建应用程序。由于这是我第一次真正深入研究这个 material,所以我可能遗漏了一些非常简单的东西。完整代码可以在 https://github.com/Afro523/MineralID-Meteor.
找到
我在 mongo 中有一个名为 Minerals 的 collection,我很确定数据本身通过观看流星开发工具 ddp 和 mini[=42 从发布加载到订阅=].
我正在使用 React 路由器转到 ListPage.jsx,然后加载我的 collection 矿物。我 运行 我的代码(如下)并得到 "Uncaught TypeError: Cannot read property 'minName' of undefined"
我希望我提供了足够详细的情况,非常感谢任何和所有建议。先谢谢你了!
ListPage.jsx 即着陆页
import React, { Component, PropTypes } from 'react';
import baseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import {List} from 'material-ui/List';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
import {Link} from 'react-router';
import {createContainer} from 'meteor/react-meteor-data';
import {Meteor} from 'meteor/meteor';
import {Minerals} from '../../api/minerals';
import MinList from './MinList';
// App component - represents the whole app
export class ListPage extends Component {
constructor(props) {
super(props);
}
getChildContext() {
return { muiTheme: getMuiTheme(baseTheme) };
}
renderMinerals () {
return this.props.minerals.map((mineral) => (
<MinList key={mineral._id} mineral={mineral}/>
));
}
render() {
return (
<div className="container">
<AppBar
iconElementLeft={<IconButton><Link to="/"><NavigationClose/></Link></IconButton>}
title="Mineral ID"
/>
<List>
{ this.renderMinerals() }
</List>
</div>
);
}
}
ListPage.propTypes = {
minerals: PropTypes.array.isRequired,
};
export default createContainer(()=>{
if (Meteor.subscribe('minerals').ready()){
return{
minerals: Minerals.find({}, {sort: {name: 1}, limit:10}).fetch(),
};
} else {
return{
minerals: null
};
}
}, MinList);
ListPage.childContextTypes = {
muiTheme: React.PropTypes.object.isRequired,
};
MinList.jsx
import React, {Component, PropTypes} from 'react';
import {ListItem} from 'material-ui/List';
import Avatar from 'material-ui/Avatar';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import MinCard from './MinCard';
const customContentStyle = {
width: '95%',
maxWidth: 'none',
};
export default class MinList extends Component {
constructor(props) {
super(props);
//Setting up state for dialog
this.state = {
open:false
};
}
handleOpen() {
this.setState({open: true});
}
handleClose() {
this.setState({open: false});
}
render() {
const mineral = this.props.mineral;
const actions = [
<FlatButton
label="Close"
primary={true}
onTouchTap={this.handleClose.bind(this)}
/>,
];
return (
<div>
<ListItem
primaryText={mineral.minName}
leftAvatar={<Avatar src={'./img/'+mineral.minName+'.jpg'}/>}
secondaryText={mineral.formula}
onTouchTap={this.handleOpen.bind(this)}
/>
<Dialog
title={mineral.minName}
leftAvatar={<Avatar src="minImage.jpg"/>}
actions={actions}
modal={false}
open={this.state.open}
onRequestClose={this.handleClose.bind(this)}
autoScrollBodyContent={true}
contentStyle={customContentStyle}
>
<MinCard mineral={mineral}/>
</Dialog>
</div>
);
}
}
MinList.propTypes ={
minerals: PropTypes.array.isRequired,
mineral : PropTypes.object.isRequired,
};
MinCard.jsx 最低级组件
import React, {Component, PropTypes} from 'react';
import {Card, CardMedia, CardText} from 'material-ui/Card';
import {Table, TableBody, TableRow, TableRowColumn} from 'material-ui/Table';
const tableStyle={
fontSize: '15px',
};
export default class MinCard extends Component {
render() {
const mineral = this.props.mineral;
return (
<Card>
<CardMedia mediaStyle={{height: '50%', width: '50%', margin: 'auto'}}>
<img src={'./img/'+this.props.mineral.minName+'.jpg'}/>
</CardMedia>
<CardText>
<h5>Summary</h5>
{mineral.summary}
</CardText>
<Table>
<TableBody
displayRowCheckbox={false}
>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Formula
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.formula}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Crystal System
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.crystalSystem}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Crystal Habit
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.crystalHabit}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Cleavage
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.cleavage}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Luster
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.luster}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Color
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.color}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Streak
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.streak}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Class Type
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.classType}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Fracture
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.fracture}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Hardness
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.hardness}
</TableRowColumn>
</TableRow>
</TableBody>
</Table>
</Card>
);
}
}
MinCard.propTypes = {
mineral: PropTypes.object.isRequired,
};
我认为您遇到的问题源于这段代码:
renderMinerals () {
return this.props.minerals.map((mineral) => (
<MinList key={mineral._id} mineral={mineral}/>
));
}
您的 map
函数没有 returning 任何东西,因此 renderMinerals 函数将 return 一个 [undefined, undefined, undefined]
.
的数组
试试 return (<MinList key={mineral._id} mineral={mineral}/>);
编辑:另一个技巧是在 MinList
的渲染函数中放置一个断点,并检查 this.props.mineral
不是 undefined
首先感谢 Paqash 为我指明了正确的方向,问题是组件在数据可用之前安装。所以经过一番研究,我发现了这个话题 https://forums.meteor.com/t/react-component-mount-wait-for-subscriptions-ready/13646.
我需要一个 getMeteorData() 函数,从那里我可以控制订阅准备就绪和未准备好时发生的情况。由于这个功能,我还不得不将地图功能更改为 this.data.minerals,更改后的页面如下。我希望这可以帮助其他人寻找解决此问题的方法。
import React, { Component, PropTypes } from 'react';
import baseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import {List} from 'material-ui/List';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
import {Link} from 'react-router';
import {ReactMeteorData} from 'meteor/react-meteor-data';
import {Meteor} from 'meteor/meteor';
import {Minerals} from '../../api/minerals';
import MinList from './MinList';
import ReactMixin from 'react-mixin';
export default class ListPage extends Component {
constructor(props) {
super(props);
}
getChildContext() {
return { muiTheme: getMuiTheme(baseTheme) };
}
//New Function Needed
getMeteorData(){
const handle = Meteor.subscribe('minerals');
return {
ready: handle.ready(),
minerals: Minerals.find({}, {sort: {name: 1}}).fetch(),
};
}
renderMinerals () {
return this.data.minerals.map((mineral) => (
<MinList key={mineral._id} mineral={mineral}/>
));
}
render() {
//Wrapped render in if data ready bool
if(!this.data.ready){
return (
<div className="container">
<AppBar
iconElementLeft={<IconButton><Link to="/"><NavigationClose/></Link></IconButton>}
title="Mineral ID"
/>
<div>Loading</div>
</div>
);
} else {
return (
<div className="container">
<AppBar
iconElementLeft={<IconButton><Link to="/"><NavigationClose/></Link></IconButton>}
title="Mineral ID"
/>
<List>
{ this.renderMinerals() }
</List>
</div>
);
}
}
}
//Added
ReactMixin(ListPage.prototype, ReactMeteorData);
ListPage.propTypes = {
minerals: PropTypes.array.isRequired,
};
ListPage.childContextTypes = {
muiTheme: React.PropTypes.object.isRequired,
};
所以我一直在尝试自学 react 和 meteor 来创建应用程序。由于这是我第一次真正深入研究这个 material,所以我可能遗漏了一些非常简单的东西。完整代码可以在 https://github.com/Afro523/MineralID-Meteor.
找到我在 mongo 中有一个名为 Minerals 的 collection,我很确定数据本身通过观看流星开发工具 ddp 和 mini[=42 从发布加载到订阅=].
我正在使用 React 路由器转到 ListPage.jsx,然后加载我的 collection 矿物。我 运行 我的代码(如下)并得到 "Uncaught TypeError: Cannot read property 'minName' of undefined"
我希望我提供了足够详细的情况,非常感谢任何和所有建议。先谢谢你了!
ListPage.jsx 即着陆页
import React, { Component, PropTypes } from 'react';
import baseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import {List} from 'material-ui/List';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
import {Link} from 'react-router';
import {createContainer} from 'meteor/react-meteor-data';
import {Meteor} from 'meteor/meteor';
import {Minerals} from '../../api/minerals';
import MinList from './MinList';
// App component - represents the whole app
export class ListPage extends Component {
constructor(props) {
super(props);
}
getChildContext() {
return { muiTheme: getMuiTheme(baseTheme) };
}
renderMinerals () {
return this.props.minerals.map((mineral) => (
<MinList key={mineral._id} mineral={mineral}/>
));
}
render() {
return (
<div className="container">
<AppBar
iconElementLeft={<IconButton><Link to="/"><NavigationClose/></Link></IconButton>}
title="Mineral ID"
/>
<List>
{ this.renderMinerals() }
</List>
</div>
);
}
}
ListPage.propTypes = {
minerals: PropTypes.array.isRequired,
};
export default createContainer(()=>{
if (Meteor.subscribe('minerals').ready()){
return{
minerals: Minerals.find({}, {sort: {name: 1}, limit:10}).fetch(),
};
} else {
return{
minerals: null
};
}
}, MinList);
ListPage.childContextTypes = {
muiTheme: React.PropTypes.object.isRequired,
};
MinList.jsx
import React, {Component, PropTypes} from 'react';
import {ListItem} from 'material-ui/List';
import Avatar from 'material-ui/Avatar';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import MinCard from './MinCard';
const customContentStyle = {
width: '95%',
maxWidth: 'none',
};
export default class MinList extends Component {
constructor(props) {
super(props);
//Setting up state for dialog
this.state = {
open:false
};
}
handleOpen() {
this.setState({open: true});
}
handleClose() {
this.setState({open: false});
}
render() {
const mineral = this.props.mineral;
const actions = [
<FlatButton
label="Close"
primary={true}
onTouchTap={this.handleClose.bind(this)}
/>,
];
return (
<div>
<ListItem
primaryText={mineral.minName}
leftAvatar={<Avatar src={'./img/'+mineral.minName+'.jpg'}/>}
secondaryText={mineral.formula}
onTouchTap={this.handleOpen.bind(this)}
/>
<Dialog
title={mineral.minName}
leftAvatar={<Avatar src="minImage.jpg"/>}
actions={actions}
modal={false}
open={this.state.open}
onRequestClose={this.handleClose.bind(this)}
autoScrollBodyContent={true}
contentStyle={customContentStyle}
>
<MinCard mineral={mineral}/>
</Dialog>
</div>
);
}
}
MinList.propTypes ={
minerals: PropTypes.array.isRequired,
mineral : PropTypes.object.isRequired,
};
MinCard.jsx 最低级组件
import React, {Component, PropTypes} from 'react';
import {Card, CardMedia, CardText} from 'material-ui/Card';
import {Table, TableBody, TableRow, TableRowColumn} from 'material-ui/Table';
const tableStyle={
fontSize: '15px',
};
export default class MinCard extends Component {
render() {
const mineral = this.props.mineral;
return (
<Card>
<CardMedia mediaStyle={{height: '50%', width: '50%', margin: 'auto'}}>
<img src={'./img/'+this.props.mineral.minName+'.jpg'}/>
</CardMedia>
<CardText>
<h5>Summary</h5>
{mineral.summary}
</CardText>
<Table>
<TableBody
displayRowCheckbox={false}
>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Formula
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.formula}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Crystal System
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.crystalSystem}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Crystal Habit
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.crystalHabit}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Cleavage
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.cleavage}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Luster
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.luster}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Color
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.color}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Streak
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.streak}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Class Type
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.classType}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Fracture
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.fracture}
</TableRowColumn>
</TableRow>
<TableRow
selectable={false}
>
<TableRowColumn style={tableStyle}>
Hardness
</TableRowColumn>
<TableRowColumn style={tableStyle}>
{mineral.hardness}
</TableRowColumn>
</TableRow>
</TableBody>
</Table>
</Card>
);
}
}
MinCard.propTypes = {
mineral: PropTypes.object.isRequired,
};
我认为您遇到的问题源于这段代码:
renderMinerals () {
return this.props.minerals.map((mineral) => (
<MinList key={mineral._id} mineral={mineral}/>
));
}
您的 map
函数没有 returning 任何东西,因此 renderMinerals 函数将 return 一个 [undefined, undefined, undefined]
.
试试 return (<MinList key={mineral._id} mineral={mineral}/>);
编辑:另一个技巧是在 MinList
的渲染函数中放置一个断点,并检查 this.props.mineral
不是 undefined
首先感谢 Paqash 为我指明了正确的方向,问题是组件在数据可用之前安装。所以经过一番研究,我发现了这个话题 https://forums.meteor.com/t/react-component-mount-wait-for-subscriptions-ready/13646.
我需要一个 getMeteorData() 函数,从那里我可以控制订阅准备就绪和未准备好时发生的情况。由于这个功能,我还不得不将地图功能更改为 this.data.minerals,更改后的页面如下。我希望这可以帮助其他人寻找解决此问题的方法。
import React, { Component, PropTypes } from 'react';
import baseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import {List} from 'material-ui/List';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
import {Link} from 'react-router';
import {ReactMeteorData} from 'meteor/react-meteor-data';
import {Meteor} from 'meteor/meteor';
import {Minerals} from '../../api/minerals';
import MinList from './MinList';
import ReactMixin from 'react-mixin';
export default class ListPage extends Component {
constructor(props) {
super(props);
}
getChildContext() {
return { muiTheme: getMuiTheme(baseTheme) };
}
//New Function Needed
getMeteorData(){
const handle = Meteor.subscribe('minerals');
return {
ready: handle.ready(),
minerals: Minerals.find({}, {sort: {name: 1}}).fetch(),
};
}
renderMinerals () {
return this.data.minerals.map((mineral) => (
<MinList key={mineral._id} mineral={mineral}/>
));
}
render() {
//Wrapped render in if data ready bool
if(!this.data.ready){
return (
<div className="container">
<AppBar
iconElementLeft={<IconButton><Link to="/"><NavigationClose/></Link></IconButton>}
title="Mineral ID"
/>
<div>Loading</div>
</div>
);
} else {
return (
<div className="container">
<AppBar
iconElementLeft={<IconButton><Link to="/"><NavigationClose/></Link></IconButton>}
title="Mineral ID"
/>
<List>
{ this.renderMinerals() }
</List>
</div>
);
}
}
}
//Added
ReactMixin(ListPage.prototype, ReactMeteorData);
ListPage.propTypes = {
minerals: PropTypes.array.isRequired,
};
ListPage.childContextTypes = {
muiTheme: React.PropTypes.object.isRequired,
};