【发布时间】:2020-02-11 12:15:13
【问题描述】:
我正在学习 ReactJS,现在正在为自己做一个小项目来管理我的食谱。
我使用npx create-react-app <my-app> 创建我的项目。我展示了我的食谱库,每个食谱都有图像、名称、描述和准备时间。通过单击食谱,将打开另一个页面,其中包含食谱的详细信息以及成分和准备步骤的列表。我正在运行图库页面,单击食谱时会打开详细信息页面,但它没有获得我传递给它的食谱的属性。
我的应用程序/项目组件: 应用.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { Switch, Route } from 'react-router-dom'
import HomePage from './pages/HomePage';
import LoginPage from './pages/LoginPage';
import RecipesPage from './pages/RecipesPage';
import RecipeDetailsPage from './pages/RecipeDetailsPage';
import PlannedDinnerPage from './pages/PlannedDinnerPage';
import ShoppingListPage from './pages/ShoppingListPage';
import jsonUsers from './data/users'
import jsonKitchens from './data/Kitchens'
import jsonDishTypes from './data/DishTypes'
import jsonIngredients from './data/Ingredient'
import jsonRecipes from './data/recipes'
import jsonRecipeIngredients from './data/RecipeIngrediaent'
import jsonRecipesPreperationSteps from './data/RecipePreperationStep'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
recipeId: null,
activeUser: null,
allUsers: jsonUsers,
allKitchens: jsonKitchens,
allDishTypes: jsonDishTypes,
allIngredients: jsonIngredients,
allRecipes: jsonRecipes,
allRecipesIngredients: jsonRecipeIngredients,
allRecipesPreperationSteps: jsonRecipesPreperationSteps,
activeUserRecipes: []
// hack for starting with my recipes
// activeUserRecipes: jsonRecipes.filter(recipe => recipe.userId === 1)
}
this.handleLogout = this.handleLogout.bind(this);
this.handleLogin = this.handleLogin.bind(this);
this.addRecipe = this.addRecipe.bind(this);
console.log(this.state.allRecipes);
}
handleLogout() {
this.setState({ activeUser: null });
}
handleLogin(activeUser) {
const activeUserRecipes = this.state.allRecipes.filter(recipe => recipe.userId === activeUser.id)
this.setState({ activeUser, activeUserRecipes });
}
addRecipe(newRecipe) {
//const {activeUser, allRecipes, activeUserRecipes} this.state.activeUser
// 1) add id and user to the recipe
newRecipe.userId = this.state.activeUser.id;
newRecipe.id = this.state.allRecipes[this.state.allRecipes.length - 1].id + 1;
// 2) update all recipes and active user recipes
const allRecipes = this.state.allRecipes.concat(newRecipe);
const activeUserRecipes = this.state.activeUserRecipes.concat(newRecipe);
this.setState({ allRecipes, activeUserRecipes });
}
render() {
// const activeUser = this.state.activeUser;
const { recipeId, activeUser, allUsers,
allRecipes,
allKitchens,
allDishTypes,
allIngredients,
allRecipesIngredients,
allRecipesPreperationSteps,
activeUserRecipes } = this.state;
return (
<Switch>
<Route exact path="/">
<HomePage activeUser={activeUser} handleLogout={this.handleLogout} />
</Route>
<Route path="/login">
<LoginPage users={allUsers} handleLogin={this.handleLogin} />
</Route>
<Route exact path="/recipes">
<RecipesPage activeUser={activeUser} addRecipe={this.addRecipe} allRecipes={allRecipes} handleLogout={this.handleLogout} userRecipes={activeUserRecipes} />
</Route>
<Route path="/recipes/:id">
<RecipeDetailsPage activeUser={activeUser} addRecipe={this.addRecipe} allRecipes={allRecipes} handleLogout={this.handleLogout} userRecipes={activeUserRecipes} />
</Route>
<Route path="/dinner">
<PlannedDinnerPage activeUser={activeUser} addRecipe={this.addRecipe} allRecipes={activeUserRecipes} handleLogout={this.handleLogout} />
</Route>
<Route path="/shopping">
<ShoppingListPage activeUser={activeUser} addRecipe={this.addRecipe} allRecipes={allRecipes} handleLogout={this.handleLogout} userRecipes={activeUserRecipes} />
</Route>
</Switch>
);
}
}
export default App;
RecipesPage.js 从“反应”导入反应 从'../components/RecipesNavbar'导入RecipesNavbar 从 'react-bootstrap' 导入 { Container, Row, Col, Button, Modal, Form } 从'react-router-dom'导入{重定向} 从'../components/RecipeCard'导入RecipeCard
class RecipesPage extends React.Component {
constructor(props) {
super(props);
this.state = {
navigateToRecipeId: null,
showModal: false
}
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
this.createRecipe = this.createRecipe.bind(this);
this.openRecipeDetails = this.openRecipeDetails.bind(this);
this.nameInput = React.createRef();
this.descInput = React.createRef();
this.imgInput = React.createRef();
}
openRecipeDetails() {
let navigateToRecipeId = this.props.recipeData.id;
this.setState({ navigateToRecipeId });
}
openModal() {
this.setState({ showModal: true })
}
closeModal() {
this.setState({ showModal: false })
}
createRecipe() {
const newRecipe = {
name: this.nameInput.current.value,
desc: this.descInput.current.value,
img: this.imgInput.current.value,
}
this.props.addRecipe(newRecipe);
this.closeModal();
}
/*
activeUser={activeUser}
addRecipe={this.addRecipe}
allRecipes={allRecipes}
handleLogout={this.handleLogout}
recipe={recipe}
recipeId={recipeId}
userRecipes={activeUserRecipes}
*/
render() {
const { activeUser, handleLogout, allRecipes } = this.props;
const { recipeId, userRecipes } = this.props;
const { showModal } = this.state;
if (!activeUser) {
return <Redirect to="/" />
}
const recipesCards = allRecipes.map(recipe =>
<Col key={recipe.id} lg="3" md="6">
<RecipeCard activeUser={activeUser} recipe={recipe} recipeData={recipe} />
</Col>
);
return (
<div>
<RecipesNavbar activeUser={activeUser} handleLogout={handleLogout} />
<Container>
<div className="recipes-header">
<h1>{activeUser.fname}'s Recipes</h1>
<Button variant="primary" onClick={this.openModal}>New Recipe</Button>
</div>
<Row>
{recipesCards}
</Row>
</Container>
<Modal show={showModal} onHide={this.closeModal} size="lg">
<Modal.Header closeButton>
<Modal.Title>New Recipe</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group as={Row} controlId="formHorizontalEmail">
<Form.Label column sm={2}>
Name
</Form.Label>
<Col sm={10}>
<Form.Control ref={this.nameInput} type="text" placeholder="Recipe name" />
</Col>
</Form.Group>
<Form.Group as={Row} controlId="formHorizontalPassword">
<Form.Label column sm={2}>
Description
</Form.Label>
<Col sm={10}>
<Form.Control ref={this.descInput} type="text" placeholder="Recipe description" />
</Col>
</Form.Group>
<Form.Group as={Row} controlId="formHorizontalPassword">
<Form.Label column sm={2}>
Image URL
</Form.Label>
<Col sm={10}>
<Form.Control ref={this.imgInput} type="text" placeholder="Recipe image URL" />
</Col>
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.closeModal}>
Close
</Button>
<Button variant="primary" onClick={this.createRecipe}>
Create Recipe
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
export default RecipesPage;
RecipeCard.js
import React from 'react'
import { Card } from 'react-bootstrap'
import { Redirect } from 'react-router-dom'
class RecipeCard extends React.Component {
constructor(props) {
super(props);
this.state = {
navigateToRecipeId: null,
recipeId: null,
recipe: null,
recipeData: null,
}
this.openRecipeDetails = this.openRecipeDetails.bind(this);
}
openRecipeDetails() {
console.log("openRecipeDetails - RecipeId: " + this.props.recipe.id);
let { navigateToRecipeId, recipe, recipeData } = this.state;
navigateToRecipeId = this.props.recipe.id;
recipe = this.props.recipe;
recipeData = this.props.recipe;
// this.state.recipeDetails = from data files / database;
this.setState({ navigateToRecipeId, recipeData });
}
render() {
const { activeUser, recipe, activeUserRecipes, recipeData } = this.props;
const { navigateToRecipeId } = this.state;
/*
activeUser={activeUser}
addRecipe={this.addRecipe}
allRecipes={allRecipes}
handleLogout={this.handleLogout}
recipe={recipe}
recipeId={recipeId}
userRecipes={activeUserRecipes}
*/
if (this.state.navigateToRecipeId != null) {
const { navigateToRecipeId } = this.state;
return (
<Redirect to={'/recipes/' + this.state.navigateToRecipeId} activeUser={activeUser} recipe={recipeData} recipeData={recipeData} recipeId={navigateToRecipeId} recipe={recipe} userRecipes={activeUserRecipes} />
);
} else {
return (
<Card className="recipe" >
<Card.Img onClick={this.openRecipeDetails} variant="top" src={recipeData.img} />
<Card.Body>
<Card.Title>{recipeData.name}</Card.Title>
<Card.Subtitle>{recipeData.desc}</Card.Subtitle>
<Card.Text>Cooking Time: {recipeData.duration} min</Card.Text>
</Card.Body>
</Card>
);
}
}
}
export default RecipeCard;
RecipeDetailsPage.js
import React from 'react'
import { Jumbotron, Button, Container } from 'react-bootstrap'
import RecipesNavbar from '../components/RecipesNavbar';
class RecipeDetailsPage extends React.Component {
constructor(props) {
super(props);
this.state = {
navigateToRecipeId: null,
recipeId: null,
recipe: null,
recipeData: null,
}
}
/*
activeUser={activeUser}
allRecipes={allRecipes}
userRecipes={activeUserRecipes}
recipeId={recipeId}
handleLogout={this.handleLogout}
addRecipe={this.addRecipe}
*/
componentDidMount() {
console.log("RecipeDetailsPage.componentDidMount() -- recipeId = ?");
console.log("RecipeDetailsPage.componentDidMount() -- recipeId: " + this.props.recipe.id);
let recipeId = this.props.match.params.id;
}
render() {
const { activeUser, handleLogout } = this.props;
return (
<div>
<RecipesNavbar activeUser={activeUser} handleLogout={handleLogout} />
<Jumbotron>
<Container>
<h1 className="display-3">Show Details of Selected Recipe</h1>
<div>Name: < R e c i p e N a m e ></div>
<div>Description: < R e c i p e D e s c r i p t i o n ></div>
<div>Duration: < (nnn) min ></div>
<div>Kitchen: < K i t c h e n i t B e l o n g s T o ></div>
<div>Type: < R e c i p e T y p e ></div>
</Container>
</Jumbotron>
</div>
);
}
}
export default RecipeDetailsPage;
在RecipeCard.js 的Redirect 中,我看到了我要发送的属性的值,但是在constructor 的RecipeDetailsPage.js 中,在调用super(props) 之后,我没有看到我发送的属性重定向,只有我在 RecipeDetailsPage 组件的 Route 中的 App.js 中拥有的那些。
我正在 google 中寻找提示,但到目前为止我发现的只是关于使用 Route 的属性(我仍在搜索)。
我做错了什么?我错过了什么?我的做法是正确的还是应该以不同的方式来做?
【问题讨论】:
-
我不认为通过重定向向组件发送道具是一个好主意,我的意思是你的应用程序的逻辑会像那样超级混乱。为什么不使用 redux 或 context api 将一些数据从一个地方传递到另一个地方,就像你描述的那样。无需依赖重定向
-
我不会为此目的使用重定向。听起来您想在食谱卡上使用Link 组件来导航到食谱页面。加载食谱页面时,使用 url 中的
:id参数获取食谱数据。
标签: reactjs react-router create-react-app react-router-dom