【问题标题】:How to pass properties from parent to child in Redirect如何在重定向中将属性从父级传递给子级
【发布时间】: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: &lt; R e c i p e   N a m e &gt;</div>
                                    <div>Description:  &lt; R e c i p e   D e s c r i p t i o n &gt;</div>
                                    <div>Duration:  &lt; (nnn) min &gt;</div>
                                    <div>Kitchen:  &lt; K i t c h e n   i t   B e l o n g s   T o &gt;</div>
                                    <div>Type:  &lt; R e c i p e   T y p e &gt;</div>
                              </Container>
                         </Jumbotron>
                    </div>
              );
         }
    }

    export default RecipeDetailsPage;

RecipeCard.jsRedirect 中,我看到了我要发送的属性的值,但是在constructorRecipeDetailsPage.js 中,在调用super(props) 之后,我没有看到我发送的属性重定向,只有我在 RecipeDetailsPage 组件的 Route 中的 App.js 中拥有的那些。

我正在 google 中寻找提示,但到目前为止我发现的只是关于使用 Route 的属性(我仍在搜索)。

我做错了什么?我错过了什么?我的做法是正确的还是应该以不同的方式来做?

【问题讨论】:

  • 我不认为通过重定向向组件发送道具是一个好主意,我的意思是你的应用程序的逻辑会像那样超级混乱。为什么不使用 redux 或 context api 将一些数据从一个地方传递到另一个地方,就像你描述的那样。无需依赖重定向
  • 我不会为此目的使用重定向。听起来您想在食谱卡上使用Link 组件来导航到食谱页面。加载食谱页面时,使用 url 中的 :id 参数获取食谱数据。

标签: reactjs react-router create-react-app react-router-dom


【解决方案1】:

谢谢John RuddellDennis Martinez,你们给了我非常有用的提示,让我找到了我需要的解决方案。

我没有在这个项目中使用Redux,因为这是我接下来要学习的东西,在这个练习中我想练习react-routerreact-router-dom。我在应用程序的另一部分使用context API,使用Link 组件也是如此。

这是我发现可行的解决方案:

App.js

    {/* Commenting the old code, followed by the code that works;
    <Route path="/recipes/:id">
        <RecipeDetailsPage activeUser={activeUser} addRecipe={this.addRecipe} allRecipes={allRecipes} handleLogout={this.handleLogout} userRecipes={activeUserRecipes} />
    </Route> 
    */}
    <Route path="/recipes/:id" component={RecipeDetailsPage} activeUser={activeUser} allRecipes={allRecipes} />

当在RecipeDetailsPage 中使用&lt;Route path=".." component={..}... /&gt; 时,我在this.props.match.params.id 中得到:id,并且可以使用REST API GET 请求从数据库中检索我需要的其余详细信息。这正是我所需要的。

【讨论】:

  • 很高兴你知道了。作为旁注,您可以使用您拥有的原始路由代码并使用 react-router-dom 提供的hook api 提供程序来访问:id 参数const { id } = useParams(),如果您想仍然使用这种方式,实际上是受到 react-router-dom 开发者的鼓励。在他们的v5.1 blog post 中了解更多信息
  • 我在学习 React 时缺少的是最佳实践方面,开发人员如何做事?以及如何建议以正确的方式做事?有很多教程,但涉及这个主题的教程并不多。
猜你喜欢
  • 2021-06-28
  • 2018-01-15
  • 2021-10-18
  • 1970-01-01
  • 2022-09-30
  • 1970-01-01
  • 1970-01-01
  • 2020-07-21
  • 2017-11-07
相关资源
最近更新 更多