React Router - Path with :id is not working correctly for component wrapped by HOC

不问归期 提交于 2021-01-28 06:11:21

问题


Hi I have been developing this application using react and react-router-dom The Page component is wrapped by a HOC that imports a contentservice to access a rest api.

My navigation is in the App component. The relevant part is the

<Link to="/page/123">About Page</Link>

and

<Link to="/page/456">Contact Page</Link>

When these links are clicked the page doesn't redraw as i expected. First time i go to 'About Page' it's all good. Then when i click to go to 'Contact Page' nothing changes. Then i click on the 'About Page' again and the 'Contact Page' shows.

In all the cases above the browser address bar shows the right path and if i refresh the page i go to the right page.

Here is my navigation page:

import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import { connect } from "react-redux";
import Home from "./Home";
import Admin from "./Admin";
import Members from "./Members";
import Login from "./Login";
import Page from "./Page";
import PrivateRoute from "./PrivateRoute";

import "./App.css";

class App extends Component {
  render() {
    return (
      <Router>
        <div>
          <ul>
            <li>
              <Link to="/">Home Page</Link>
            </li>
            <li>
              <Link to="/page/123">About Page</Link>
            </li>
            <li>
              <Link to="/page/456">Contact Page</Link>
            </li>
            <li>
              <Link to="/members">Members</Link>
            </li>
            <li>
              <Link to="/admin">Admin</Link>
            </li>
          </ul>
        </div>
        <Switch>
          <Route path="/login" component={Login} />
          <Route path="/page/:id" component={Page} />
          <Route exact path="/" component={Home} />
          <PrivateRoute path="/members">
            <Members />
          </PrivateRoute>
          <PrivateRoute path="/admin">
            <Admin />
          </PrivateRoute>
        </Switch>
      </Router>
    );
  }
}
const mapStateToProps = (state) => {
  return {
    isLoggedIn: state.isLoggedIn,
  };
};

export default connect(mapStateToProps, null)(App);

This is my page component:

import React, { Component } from "react";
import WithBackend from "./WithBackend";

class Page extends Component {
  constructor(props) {
    super(props);
    this.resource = "/pages/";
    this.state = { model: null };
  }
  render() {
    if (this.state.model != null) {
      return (
        <div className="container">
          <div className="row">
            <div className="col-md">
              <h1>{this.state.model.title}</h1>
              <h2 dangerouslySetInnerHTML={{ __html: this.state.model.body }} />
            </div>
          </div>
        </div>
      );
    } else {
      return (
        <div>
          <h2>Page id: {this.props.match.params.id}</h2>
        </div>
      );
    }
  }

  componentDidMount() {
    this.props
      .getEntity(this.resource, this.props.match.params.id)
      .then((model) => this.setState({ model }));
  }

  componentDidUpdate(nextProps) {
    if (nextProps.match.params.id !== this.props.match.params.id) {
      this.props
        .getEntity(this.resource, nextProps.match.params.id)
        .then((data) => {
          this.setState({ model: data });
        });
    }
  }
}

export default WithBackend(Page);

This is the Withbackend HOC:

import React from "react";
import ContentService from "./ContentService";

const WithBackend = (WrappedComponent) => {
  class HOC extends React.Component {
    constructor() {
      super();
      this.contentService = new ContentService();
      this.getEntity = this.getEntity.bind(this); 
      this.getEntities = this.getEntities.bind(this); 
    }  

    getEntity(resource, id) {
      return this.contentService
        .getEntity(resource, id)
        .then((response) => response.json())
        .catch((e) => {
          console.log(e);
        });
    }

    getEntities(resource) {
      return this.contentService
        .getEntities(resource)
        .then((response) => response.json())
        .catch((e) => {
          console.log(e);
        });
    }

    render() {
      return (
        <WrappedComponent
          getEntity={this.getEntity}
          getEntities={this.getEntities}
          {...this.props}
        />
      );
    }
  }
  return HOC;
};

export default WithBackend;

And the content service:

class ContentService {
  baseUrl = "http://localhost:8080";

  getEntity(resource, id) {
    const path = this.baseUrl + resource + id;
    const fetchPromise = fetch(path, {
      method: "GET",
    });

    return Promise.resolve(fetchPromise);
  }

  getEntities(resource) {
    const fetchPromise = fetch(this.baseUrl + resource, {
      method: "GET",
    });

    return Promise.resolve(fetchPromise);
  }
}

export default ContentService;

Has anyone got any ideas why this is happening? I am not sure if it has anything to do with the Page component being wrapped by HOC but just thought it is worth mentioning.

Thank you.


回答1:


Issue

The componentDidUpdate lifecycle method receives the previous props, state, and snapshot values, not the next ones.

componentDidUpdate

componentDidUpdate(prevProps, prevState, snapshot)

By sending the "previous" props' match param id you were a "cycle" behind.

Solution

Use the current id value from props.

componentDidUpdate(prevProps) {
  if (prevProps.match.params.id !== this.props.match.params.id) {
    this.props
      .getEntity(this.resource, this.props.match.params.id)
      .then((data) => {
        this.setState({ model: data });
      });
  }
}


来源:https://stackoverflow.com/questions/65657309/react-router-path-with-id-is-not-working-correctly-for-component-wrapped-by-h

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!