Рендеринг компонента сработал, но DOM не обновлен - PullRequest
1 голос
/ 02 апреля 2019

У меня проблемы с моим первым приложением React.На практике у меня есть иерархия компонентов (я создаю галерею мультимедийных фильмов), которая при нажатии на вкладку (представленную компонентом «Фильм») должна отображать конкретное описание отдельного фильма (SingleMovieDetails).Проблема в том, что DOM обновляется только при первом щелчке, а затем, даже если изменяется реквизит SingleMovieDetails, DOM остается заблокированным в первом отрендеренном фильме.

Вот код, который я написал до сих пор ...

//Movie component
import React from "react";
import styles from "./Movie.module.scss";
import PropTypes from "prop-types";

class Movie extends React.Component{
  constructor(props){
    super(props);
    this.imgUrl = `http://image.tmdb.org/t/p/w342/${this.props.movie.poster_path}`;
  }

  render(){
    if(!this.props.size)
    return <div onClick={this.props.callbackClick(this.props.movie.id)}
                name={this.props.movie.id}
                className={styles.movieDiv}
                style={{backgroundImage: `url(${this.imgUrl})`}}></div>;
    return <div onClick={() => this.props.callbackClick(this.props.movie.id)}
                name={this.props.movie.id}
                className={styles.movieDivBig}
                style={{backgroundImage: `url(${this.imgUrl})`}}></div>;
  }
}

Movie.propTypes = {
  movie: PropTypes.any,
  callbackClick: PropTypes.any
};
export default Movie;

SingleMovieDetails.js

import React from "react";
import styles from "./SingleMovieDetails.module.scss";

import Movie from "../Movie";
import SingleMovieDescription from "../SingleMovieDescription";
import MovieCast from "../MovieCast";
import SingleMovieRatings from "../SingleMovieRatings";

class SingleMovieDetails extends React.Component{
  constructor(props){
    super(props);
    console.log(props);
    this.state = props;
    console.log('constructor', this.state.movie)
  }

  render(){
    console.log('SMD', this.state.movie)
    return (
        <>
          <div className={styles.container}>
            <div className={styles.flayer}>
               <Movie size={'big'} movie={this.state.movie}/>
            </div>
            <div className={styles.description}>
              <SingleMovieDescription movie={this.state.movie}/>
              <MovieCast></MovieCast>
            </div>
            <div className={styles.ratings}>
              <SingleMovieRatings />
            </div>
          </div>
        </>
    );
  }
}

export default SingleMovieDetails;

MovieCarousel.js

import React from "react";
import PropTypes from "prop-types";
import Movie from "../Movie";
import styles from "./MovieCarousel.module.scss";
import SingleMovieDetails from "../SingleMovieDetails";

class MovieCarousel extends React.Component {
  constructor(props) {
    super(props);
    this.state = [];
    this.callbackClickMovie = this.callbackClickMovie.bind(this);
  }

  callbackClickMovie(id) {
    const singleMovieApi = `https://api.themoviedb.org/3/movie/${id}?api_key=b6f2e7712e00a84c50b1172d26c72fe9`;
    fetch(singleMovieApi)
      .then(function(response) {
        return response.json();
      })
      .then(data => {
        this.setState({ selected: data });
      });
  }

  render() {
    let details = null;
    if (this.state.selected) {
      details = <SingleMovieDetails movie={this.state.selected} />;
    }
    let counter = 6;
    let movies = this.props.movies.map(el => {
      let element = (
        <Movie movie={el} callbackClick={this.callbackClickMovie} />
      );
      counter--;
      if (counter >= 0) return element;
      return;
    });
    let content = (
      <>
        <h2 className={styles.carouselTitle}>{this.props.title}</h2>
        {movies}
        {details}
      </>
    );
    return content;
  }
}

MovieCarousel.propTypes = {
  children: PropTypes.any
};
export default MovieCarousel;

Буду очень признателен, если кто-нибудь сможет мне помочь.Я был на этом в течение двух дней, но я не могу справиться с этим

Ответы [ 2 ]

3 голосов
/ 02 апреля 2019

Это связано с тем, что в SingleMovieDetails компоненте вы храните значения реквизитов в состоянии, а не обновляете состояние при изменении реквизита.constructor больше не будет вызываться, поэтому состояние всегда будет иметь начальные значения.

У вас есть два варианта решения проблемы:

  1. Вместо этого напрямую используйте значения реквизитахранения данных в состоянии (предпочтительный способ).Например:

    class SingleMovieDetails extends React.Component {
      constructor(props) {
        super(props);
      }
    
      render() {
         return (
           <>
           <div className={styles.container}>
            <div className={styles.flayer}>
               <Movie size={'big'} movie={this.props.movie}/>
            </div>
            <div className={styles.description}>
              <SingleMovieDescription movie={this.props.movie}/>
              <MovieCast></MovieCast>
            </div>
            <div className={styles.ratings}>
              <SingleMovieRatings />
            </div>
          </div>
         </>
        );
      }
    }
    
  2. Используйте getDerivedStateFromProps и обновите значение состояния при изменении реквизита.

Та же проблематакже с компонентом Movie, поместите эту строку в метод рендеринга, иначе он всегда будет показывать одно и то же изображение:

const imgUrl = `http://image.tmdb.org/t/p/w342/${this.props.movie.poster_path}`

И используйте эту переменную imgUrl.

3 голосов
/ 02 апреля 2019

Ваша проблема связана только с одним файлом: SingleMovieDetails.js

Внутри конструктора вы устанавливаете состояние компонента для инициализации с помощью подпорок (отправьте компоненту первымвремя)

Но внутри вашего метода render () вы ссылаетесь на это состояние снова:

<Movie size={'big'} movie={this.state.movie}/>

В общем, это не совсем неправильно, но вам нужно сделать одно из двух:

  1. Добавить метод для обновления состояния вашего компонента с помощью nextPropsReceived (Метод жизненного цикла был вызван: получит реквизиты, если вы используете последнюю версиюверсию, которую вы должны использовать: getDerivedStateFromProps)

  2. предпочтительный вариант : вам не нужно состояние для компонента фильма, поэтому просто используйте подпорки внутри функции рендеринга (это.props.movie) после этого вы также можете удалить конструктор, потому что внутри ничего особенного нет.:)

edit: Итак, просто для ясности: поскольку вы устанавливаете состояние только один раз (конструктор вызывается не при каждом обновлении жизненного цикла)у вас всегда будет только первое сохраненное значение.Изменение реквизита извне просто вызовет render (), но не запустит конструктор снова; D

...