从父组件到子路由器组件的React Router-dom传递状态显示在子组件中未定义

时间:2020-06-30 15:24:50

标签: reactjs react-router-dom

父组件 App.js 的内容,我将用户状态传递给子组件Book:-

import React, { useEffect, useState } from "react";
import NavBar from "./components/NavBar";
import Signup from "./pages/Signup";
import Home from "./pages/Home";
import Book from "./pages/Book";

function App() {
  const [user, setUser] = useState();
  useEffect(() => {
    const token = "Bearer " + sessionStorage.getItem("token");
    if (token) {
      axios
        .get(process.env.REACT_APP_API_URL + "user/self", {
          headers: {
            Authorization: token,
            "Content-Type": "application/json",
          },
        })
        .then((res) => setUser(res.data));
    }
  }, []);

 console.log("user state in App component", user);

  return (
    <div className="container-fluid">
      <NavBar user={user} />
      <Route exact path="/" component={Home} />
      <Route path="/signup" component={Signup} />
      <Route exact path="/book/:id" render={(routeProps) => <Book user={user} {...routeProps} />} />

    </div>
  );
}

export default App;

以及子组件 Book.js 的内容:-

import React, { useState, useEffect } from "react";
import axios from "axios";

const Book = (props) => {
  const [user,setUser] = useState();

  useEffect(() => {
    axios
      .get(`${process.env.REACT_APP_API_URL}book/${props.match.params.id}`)
      .then((res) => {
        //Check if user Auth
        console.log("user props.user in Book component", props.user);
        const isAuth = props.user ? props.user.id === res.user_id : false;
          }).catch((error) => {
        console.log(error);
      });
  });
}

在上面的代码没有任何问题之前,然后突然我注意到props.user在子组件中未定义。

这是控制台日志中显示的结果:-

enter image description here

1 个答案:

答案 0 :(得分:0)

答案在您的console.log中。在用户提取完成其调用之前,您的Book组件正在呈现。这是javascript异步性质的结果,也是大多数人在使用React Hook的useEffect时遗漏的怪癖的结果。该解决方案很简单,是您在所有功能组件中都应采用的一种模式。

  1. 承诺您的要求(必要时)。在这种情况下,子组件取决于axios请求的结果以正确呈现。现在,您尝试通过在useEffect中传递axios承诺来解决此问题。但是,根据设计,useEffect不会在回调中期望/返回承诺,因为这种模式会鼓励竞争条件。因此,这里是使用async/await的可靠解决方法。您可以将以下内容应用于您的应用组件,并在其中替换useEffect
useEffect(() => {
  const init = async () => {
    const token = "Bearer " + sessionStorage.getItem("token");
    
    if (token) {
      const { data } = await axios.get(
        process.env.REACT_APP_API_URL + "user/self", {
        headers: {
          Authorization: token,
          "Content-Type": "application/json",
        },
      })

      setUser(data)
    }
  }

  init();
}, []);
  1. 保护子组件免受丢失数据的影响。您在此处有几个选项,通常与一个加载指示器一起使用,以确保组件始终加载。这完全取决于预期的用户体验。

您可以阻止整个路由加载,直到提取完成。

function App() {
  const [user, setUser] = useState();
  const [loading, setLoading] = useState(false);
  ...

  // apply setLoading in useEffect

  return (
    ...
    <Route exact path="/book/:id" render={(routeProps) => loading ? <h1>LOADING</h1> : <Book user={user} {...routeProps} />} />
  )
}

您可以阻止整个子组件呈现,直到获取完成。

const Book = (props) => {
  const [user, setUser] = useState();
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const init = async () => {
      setLoading(true);
      
      try {
        const {user_id} = await axios.get(`${process.env.REACT_APP_API_URL}book/${props.match.params.id}`)
          //Check if user Auth
        console.log("user props.user in Book component", props.user);
        const isAuth = props.user ? props.user.id === user_id : false;
        setLoading(false);
      }
      catch (err) {
        console.error(err);
      }
    }

    init();
  });
}