简体   繁体   中英

React Updating parent state with API call and then update child

I want my child to rerender when my parent updates its state. Seems simple but I haven't gotten it to work.

In App.js i make the API call in componentWillMount. I then want "PrivateRoute" to update the state "auth" but it seems like componentDidUpdate never runs and I always get rediercted.

The purpose of the code is that the API call checks if the user has a valid authentication token and then sets the "isLoggedIn" state to true or false. This should make the child "PrivateRoute" either redirect (if isLoggedIn is false) or render another page (if isLoggedIn is true).

I have checked out a lot of other similar questions but no solution has worked this far.

My app.js:

import React, { Component } from "react";

import {
  BrowserRouter as Router,
  Route,
  Switch,
  Link,
  Redirect,
} from "react-router-dom";

import axios from "axios";

import PrivateRoute from "./components/PrivateRoute";

// Pages
import IndexPage from "./pages/index";
import HomePage from "./pages/home";

class App extends Component {
  constructor() {
    super();
    console.log("Constructor");
    this.state = {
      loggedInStatus: false,
      test: "TEST",
    };
  }

  // Checks if user is logged in
  checkAuth() {
    let token = localStorage.getItem("token");
    let isLoggedIn = false;
    if (token === null) token = "bad";

    console.log("Making API call");
    // API call
    axios
      .get("http://localhost:8000/authentication/checkAuth", {
        headers: { Authorization: "token " + token },
      })
      .then((res) => {
        console.log("Updateing state");
        this.setState({ loggedInStatus: true });
      })
      .catch((error) => {
        console.log("Updating state");
        this.setState({ loggedInStatus: false });
      });
    return isLoggedIn;
  }

  componentWillMount() {
    this.checkAuth();
  }

  render() {
    //console.log("Render");
    // console.log("isLoggedIn: ", this.state.loggedInStatus);

    return (
      <Router>
        <Switch>
          <PrivateRoute
            exact
            path="/home"
            component={HomePage}
            auth={this.state.loggedInStatus}
          />
          <Route exact path="/" component={IndexPage} />
        </Switch>
      </Router>
    );
  }
}

export default App;

PrivateRoute.jsx:

import { Redirect } from "react-router-dom";
import React, { Component } from "react";

class PrivateRoute extends Component {
  state = {};

  constructor(props) {
    super(props);
    this.state.auth = false;
  }

  // Update child if parent state is updated
  componentDidUpdate(prevProps) {
    console.log("Component did update");
    if (this.props.auth !== prevProps.auth) {
      console.log("Child component update");
      this.setState({ auth: this.props.auth ? true : false });
    }
  }

  render() {
    console.log("Props: ", this.props);
    console.log("State: ", this.state);
    //alert("this.props.auth: ", this.props.auth);
    //alert("TEST: ", this.props.test);
    if (this.props.auth) {
      return <h1>Success!</h1>;
      //return <Component {...this.props} />;
    } else {
      return (
        <Redirect
          to={{ pathname: "/", state: { from: this.props.location } }}
        />
      );
    }
  }
}

export default PrivateRoute;

checkAuth should be sync, if you want auth status before first-ever render in the component.

Your checkAuth will return instantly, making the auth status always false.

async checkAuth() {
    try {
        let token = localStorage.getItem("token");
        let isLoggedIn = false;
        if (token === null) token = "bad";
        const res = await axios
            .get("http://localhost:8000/authentication/checkAuth", {
                headers: {Authorization: "token " + token},
            })
        console.log("Updateing state");
        this.setState({loggedInStatus: true});
    } catch (e) {
        // for non 2XX axios will throw error
        console.log("Updating state");
        this.setState({loggedInStatus: false});
    }
}

async componentWillMount() {
    await this.checkAuth();
  }

In the child component, you have to set state from props.

constructor(props) {
    super(props);
    this.state.auth = props.auth;
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM