【发布时间】:2020-12-05 21:05:03
【问题描述】:
我正在学习反应。我的第一个 Web 应用程序是一个简单的页面,它请求有关美国的 API Covid-19 数据并将其显示为类别。我使用反应路由器来显示相同的卡片组件,其中使用 dataFinal 变量传递不同的数据,该变量保存 API 检索到的 json。如果我要访问其中一个类别,而不是首页,例如 /positiveIncrease,我会收到错误消息:
Error in /~/index.js (46:255)
Cannot read property 'positiveIncrease' of undefined
我从错误推断出渲染发生在 API 从 json 获取数据之前被检索并存储在对象中。为了解决这个问题,我尝试在卡片组件dataFinal.hospitalizedCurrently ? dataFinal.hospitalizedCurrently : "Loading..."的内容部分中使用 JSX 进行条件渲染@
但这似乎不起作用。我还尝试创建一个布尔变量来保存 API 调用是否已完成,但它也没有工作。
Stackblitz 上的应用链接是:
编辑:https://stackblitz.com/edit/react-covid-19-us-tracker?file=index.js
查看:https://react-covid-19-us-tracker.stackblitz.io
要显示的默认文件:index.js
- 要复制问题,只需通过菜单导航到 与主页不同的随机类别并重新加载页面。
API.js
import React from 'react';
let loaded=false;
let dataFinal;
fetch("https://api.covidtracking.com/v1/us/current.json")
.then(data=>data.json())
.then(data=>{
dataFinal=data[0]
})
export {dataFinal}
Card.js
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Card from "@material-ui/core/Card";
import CardActions from "@material-ui/core/CardActions";
import CardContent from "@material-ui/core/CardContent";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";
import { title, content } from "./Routing.js";
export default function Card(props) {
return (
<Card className="card">
<CardContent>
<Typography color="textSecondary" gutterBottom>
{props.title}
</Typography>
<Typography variant="h5" component="h2">
{props.content}
</Typography>
</CardContent>
<CardActions>
<Button
onClick={() => {
window.location = "https://www.cdc.gov/covid-data-tracker/#cases";
}}
size="small"
>
Learn More
</Button>
<br />
</CardActions>
{ typeof props.content === "string" || props.content instanceof String ? "" : <CardContent>
<p style={{ color: "red" }}>{repeat(props.content)}</p>
</CardContent>}
</Card>
);
}
export { Content, Title };
const repeat = num => {
if (typeof num === "string" || num instanceof String) {
return "";
}
if (num < 2000) {
return "♥ ".repeat(num);
} else {
return "♥ ".repeat(2000) + " and " + (num - 2000) + " more";
}
};
index.html
<title>Covid-19 USA Visualization</title>
<div id="root"></div>
index.js
import React, { Component} from 'react';
import {render} from 'react-dom';
import './style.css';
import {dataFinal, loaded} from "./API.js"
import Routing from "./Routing.js"
import Card from "./Card.js"
import { BrowserRouter, Route, Switch } from 'react-router-dom';
class App extends Component {
render() {
return(
<div> <Routing/>
<main>
<Switch>
<Route exact path="/" render={()=> <Card title={''} content={"Navigate through the menus on the top left"} />}>
</Route>
<Route exact path="/positive" render={()=><Card title={'Positive'} content={dataFinal.positive ? dataFinal.positive : "Loading..."} />}>
</Route>
<Route exact path="/positiveIncrease" render={()=> <Card title={'Positive increase'} content={dataFinal.positiveIncrease ? dataFinal.positiveIncrease : "Loading..."} />}>
</Route>
<Route exact path="/hospitalized" render={()=> <Card title={'Hospitalized'} content={dataFinal.hospitalized ? dataFinal.hospitalized : "Loading..."}/>}>
</Route>
<Route exact path="/hospitalizedCurrently" render={()=> <Card title={'Hospitalized currently'} content={dataFinal.hospitalizedCurrently ? dataFinal.hospitalizedCurrently : "Loading..." }/>}>
</Route>
<Route exact path="/dead" render={()=> <Card title={'Total dead'} content={dataFinal.death ? dataFinal.death : "Loading..." }/>}>
</Route>
<Route exact path="/icu" render={()=> <Card title={'Currently in intensive care unit'} content={dataFinal.inIcuCurrently ? dataFinal.inIcuCurrently : "Loading..." }/>}>
</Route>
<Route exact path="/culminativeicu" render={()=> <Card title={'Culminative in intensive care unit'} content={dataFinal.inIcuCumulative ? dataFinal.inIcuCumulative : "Loading..." }/>}>
</Route>
<Route exact path="/onventillatorsculminative" render={()=> <Card title={'Culminative on ventillators'} content={dataFinal.onVentilatorCumulative ? dataFinal.onVentilatorCumulative : "Loading..." }/>}>
</Route>
<Route exact path="/onventillatorscurrently" render={()=> <Card title={'Currently on ventillators'} content={dataFinal.onVentilatorCurrently ? dataFinal.onVentilatorCurrently : "Loading..." }/>}>
</Route>
<Route exact path="/recovered" render={()=> <Card title={'Recovered'} content={dataFinal.recovered ? dataFinal.recovered : "Loading..." }/>}>
</Route>
<Route exact path="/deadIncrease" render={()=> <Card title={'Dead Increase'} content={dataFinal.deathIncrease ? dataFinal.deathIncrease : "Loading..." }/>}>
</Route>
<Route exact path="/hospitalizedIncrease" render={()=> <Card title={'Hospitalized increase'} content={dataFinal.deathIncrease ? dataFinal.deathIncrease : "Loading..." }/>}>
</Route>
</Switch>
</main>
</div>
)
}
}
render( <BrowserRouter>
<App />
</BrowserRouter>, document.getElementById('root'));
Routing.js
import React from 'react';
import clsx from 'clsx';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import Drawer from '@material-ui/core/Drawer';
import CssBaseline from '@material-ui/core/CssBaseline';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import List from '@material-ui/core/List';
import Typography from '@material-ui/core/Typography';
import Divider from '@material-ui/core/Divider';
import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import {Content,Title} from "./Card.js"
import { BrowserRouter, Route, Switch,Link } from 'react-router-dom';
import ReplayIcon from '@material-ui/icons/Replay';
import Box from '@material-ui/core/Box';
import {retrieve} from "./API"
import 'fontsource-roboto';
const drawerWidth = 200;
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
appBar: {
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
},
menuButton: {
marginRight: theme.spacing(2),
},
hide: {
display: 'none',
},
drawer: {
width: drawerWidth,
flexShrink: 0,
},
drawerPaper: {
width: drawerWidth,
},
drawerHeader: {
display: 'flex',
alignItems: 'center',
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
justifyContent: 'flex-end',
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
marginLeft: -drawerWidth,
},
contentShift: {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginLeft: 0,
},
}));
export default function PersistentDrawerLeft() {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={clsx(classes.appBar, {
[classes.appBarShift]: open,
})}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={handleDrawerOpen}
edge="start"
className={clsx(classes.menuButton, open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
USA Covid-19 Visualization
</Typography>
</Toolbar>
</AppBar>
<Drawer
className={classes.drawer}
anchor="bottom"
open={open}
classes={{
paper: classes.drawerPaper,
}}
>
<div className={classes.drawerHeader}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
</IconButton>
</div>
<Divider />
<List>
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/"> <ListItem button key={"Home"}>
<ListItemText primary={"Home"} />
</ListItem> </Link>
<Divider />
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/positive"> <ListItem button key={"Positive"}>
<ListItemText primary={"Positive"} />
</ListItem> </Link>
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/positiveIncrease"> <ListItem button key={"PositiveIncrease"}>
<ListItemText primary={"Positive increase"} />
</ListItem> </Link>
<Divider />
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/hospitalized"> <ListItem button key={"Hospitalized"}>
<ListItemText primary={"Hospitalized culminative"} />
</ListItem></Link>
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/hospitalizedCurrently"> <ListItem button key={"hospitalizedCurrently"}>
<ListItemText primary={"Hospitalized currently"} />
</ListItem></Link>
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/hospitalizedIncrease"> <ListItem button key={"hospitalizedIncrease"}>
<ListItemText primary={"Hospitalized increase"} />
</ListItem></Link>
<Divider />
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/dead"> <ListItem button key={"Dead"}>
<ListItemText primary={"Dead Culminative"} />
</ListItem></Link>
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/deadIncrease"> <ListItem button key={"DeadIncrease"}>
<ListItemText primary={"Dead increase"} />
</ListItem></Link>
<Divider />
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/icu"> <ListItem button key={"Icu"}>
<ListItemText primary={"Currently in ICU"} />
</ListItem></Link>
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/culminativeicu"> <ListItem button key={"culminativeIcu"}>
<ListItemText primary={"In ICU culminative"} />
</ListItem></Link>
<Divider />
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/onventillatorsculminative"> <ListItem button key={"onventillatorsculminative"}>
<ListItemText primary={"On ventillators culminative"} />
</ListItem></Link>
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/onventillatorscurrently"> <ListItem button key={"onventillatorscurrently"}>
<ListItemText primary={"On ventillators currently"} />
</ListItem></Link>
<Divider />
<Link onClick={()=>setOpen(false)} style={{textDecoration:"none",color:"black"}} to="/recovered"> <ListItem button key={"recovered"}>
<ListItemText primary={"Recovered"} />
</ListItem></Link>
<Divider />
<center><Typography style={{color:"grey",fontSize:"1vmax",marginTop:"1.5vmax",marginBottom:"1vmax"}} >Created by Atanas Bobev</Typography></center>
</List>
</Drawer>
<main
className={clsx(classes.content, {
[classes.contentShift]: open,
})}
>
</main>
</div>
);
}
package.json
{
"name": "react",
"version": "0.0.0",
"private": true,
"dependencies": {
"@fortawesome/fontawesome-svg-core": "1.2.30",
"@fortawesome/react-fontawesome": "0.1.11",
"@material-ui/core": "4.11.0",
"@material-ui/icons": "4.9.1",
"@types/react": "16.9.46",
"clsx": "1.1.1",
"font-awesome": "4.7.0",
"fontsource-roboto": "^3.0.3",
"material-ui": "0.20.2",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-router-dom": "5.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"devDependencies": {
"react-scripts": "latest"
}
}
style.css
h1, p {
font-family: Lato;
}
.card{
width:90vw;
margin-left:5vw;
margin-top:5vw;
}
Link{
color:black;
text-decoration:none;
}
如何解决此路由(API)问题?我很高兴你能建议我在这个应用程序中错过的一些更好的做法。提前谢谢你。
【问题讨论】:
标签: javascript reactjs variables components