React Child componentDidMount 没有在 Route 上触发

React Child componentDidMount not triggering on Route

我是 React 的新手,一直在研究使用无头 WP 后端构建我的第一个 React 应用程序。

我无法让 componentDidMount 在我的 MoviePage class 中触发。我希望有人能帮助我理解我做错了什么。

这是我的代码,它可能并不完美,因为这是我的第一次尝试。

注意代码现在已更改为添加来自评论的建议 - 检查原始代码的编辑历史记录(它仍然损坏)

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render((
        <BrowserRouter>
            <App />
        </BrowserRouter>
    ), 
    document.getElementById('root'));
registerServiceWorker();

App.js

import React from 'react';

import Header from './components/Header';
import Main from './components/Main';

class App extends React.Component {
  render() {
    return (
      <div>
        <Header />
        <Main />
      </div>
    )
  }
}

export default App;

Header.js

import React from 'react';
import { Link } from "react-router-dom";

class Header extends React.Component {
  constructor() {
    super();
    this.state = {
      movies: []
    }
  }

  componentDidMount() {
    let dataURL = "http://localhost:8888/SBP/website/wp-json/wp/v2/movies?_embed";

    fetch(dataURL)
      .then(response => response.json())
      .then(response => {
        this.setState({
          movies: response
        })
      })
  }

  render() {
    let movieLinks = this.state.movies.map((movie, index) => {
      return <li key={index}><Link to={'/movies/'+movie.id}>{movie.title.rendered}</Link></li>
    });

    return (
      <header>
        <nav>
          <ul>
            <li><Link to='/movies'>All Movies</Link></li>
            {movieLinks}
          </ul>
        </nav>
      </header>
    )
  }  
}

export default Header;

Main.js

import React from 'react';
import { Route } from "react-router-dom";

import Movies from './Movies';

class Main extends React.Component {
  render() {
    return (
      <main>
        <Route path='/movies' component={Movies}/>
      </main>
    )
  }
}

export default Main;

Movies.js

import React from 'react';
import { Switch, Route, } from "react-router-dom";

import MoviesHub from './MoviesHub';
import MoviePage from './MoviePage';

class Movies extends React.Component {
  render() {
    return (
      <Switch>
        <Route exact path='/movies' component={MoviesHub}/>
        <Route path='/movies/:number' component={MoviePage}/>
      </Switch>
    )
  }
}

export default Movies;

MoviesHub.js

import React from 'react';

class MoviesHub extends React.Component {
  constructor() {
    super();
    this.state = {
      movies: []
    }
  }

  componentDidMount() {
    let dataURL = "http://localhost:8888/SBP/website/wp-json/wp/v2/movies?_embed";
    fetch(dataURL)
      .then(res => res.json())
      .then(res => {
        this.setState({
          movies: res
        })
      })
  }

  render() {
    let movies = this.state.movies.map((movie, index) => {
      return <div key={index}>
      <img src={movie._embedded['wp:featuredmedia'][0].media_details.sizes.large.source_url} alt={movie.title.rendered} />
      <p><strong>Title:</strong> {movie.title.rendered}</p>
      <p><strong>Release Year:</strong> {movie.acf.release_year}</p>
      <p><strong>Rating:</strong> {movie.acf.rating}</p>
      <div><strong>Description:</strong><div dangerouslySetInnerHTML={ {__html: movie.acf.description} } /></div>
      </div>
    });

    return (
      <div>
        <h2>Star Wars Movies</h2>
        {movies}
      </div>
    )
  }
}

export default MoviesHub;

MoviePage.js

import React from 'react';

class MoviePage extends React.Component {
  constructor() {
    super();
    this.state = {
      movie: []
    }
  }

  componentDidMount() {
    console.log('MoviePage componentDidMount');

    let dataURL = 'http://localhost:8888/SBP/website/wp-json/wp/v2/movies/'+this.props.match.params.number;

    fetch(dataURL)
      .then(results => {
        return results.json();
      })
      .then(data => {
        debugger;

        this.setState({
          movie: data
        })

        console.log("movie", this.state.movie);
      })
  }

  render() {
    let movie = this.state.movie;

    return <div>
        <img src={movie._embedded['wp:featuredmedia'][0].media_details.sizes.large.source_url} alt={movie.title.rendered} />
        <p><strong>Title:</strong> {movie.title.rendered}</p>
        <p><strong>Release Year:</strong> {movie.acf.release_year}</p>
        <p><strong>Rating:</strong> {movie.acf.rating}</p>
        <div><strong>Description:</strong><div dangerouslySetInnerHTML={ {__html: movie.acf.description} } /></div>
      </div>
  }
}

export default MoviePage;

作为参考,我正在 this tutorial 构建并试图将其提升到一个新的水平

任何帮助将不胜感激,任何关于如何改进我的代码以符合最佳实践的建议都将非常有用。

谢谢大家

我认为问题在于你实际上并没有将你的 React 应用程序附加到 DOM 任何地方。在某处,您需要使用 ReactDOM 将您的应用附加到页面上的某个根元素。然后,您的组件将挂载并呈现。

你可能看起来像这样:

ReactDOM.render(App, document.getElementById('my-root-element'))

查看 the documentation 了解更多信息。


编辑

我认为您可能在渲染时遇到了导致此问题的错误。如果您在初始渲染时遇到错误,您将永远无法到达 componentDidMount。

如果您查看 MoviePage class,您在初始化时设置了 movie: [] 状态。您最终会获取一部电影,这大概是一个对象。但是在您完成获取数据之前,您将调用渲染函数,它试图访问该电影的数据,例如:

movie._embedded['wp:featuredmedia'][0].media_details.sizes.large.source_url

两个快速提示:

  1. Pre-fill 你的状态与你期望你的状态 return 的类型相同。因此,如果您希望 movie 成为一个对象,则初始状态为 movie: {}.
  2. 在使用数据渲染元素之前,请确保已加载数据。在数据返回之前呈现微调器或加载消息的条件。

示例渲染函数:

render() {
  if (Object.keys(movie).length === 0) { // check if movie object is empty
    return <div>Data not loaded</div>;
  } else {
    return (
      ... whatever you had
    );
  }
}