1

我们如何componentDidMount在参数中的钩子中设置状态?

我的问题是 React notify 的警告:

Warning: Can't call setState (or forceUpdate) on an unmounted 
component. This is a no-op, but it indicates a memory leak in your 
application. To fix, cancel all subscriptions and asynchronous tasks in the 
componentWillUnmount method.
in DiscoverPage (created by Route)

在我的DiscoverPage组件中:

import React, { Component } from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import TvShows from './TvShows';
import Movies from './Movies';
import MovieDetail from "../MoviePage/MovieDetail";
import TvDetail from "../TvShowPage/TvDetail";

class DiscoverPage extends Component {

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

  getDataItem = (data) => {
    this.setState({ data: data });
  };

  render() {
    return (
      <Switch>
        <Route exact path="/discover/movie" render={props => <Movies data={this.getDataItem} {...props} />} />
        <Route path="/discover/tv" render={props => <TvShows data={this.getDataItem} {...props} />} />
        <Route path="/movie/:movie" render={props => <MovieDetail data={this.state.data} {...props} />} />
        <Route path="/tv/:tv" render={props => <TvDetail data={this.state.data} {...props} />} />
        <Redirect to="/discover/movie" />
      </Switch>
    );
  }
}

export default DiscoverPage;

在子组件中(本例为Movies组件):

import React, { Component } from 'react';
import requestApi from '../api';
import MovieList from "../MoviePage/MovieList";

class Movies extends Component {

  constructor(props) {
    super(props);
    this.state = {
      data: {
        page: 1,
        results: []
      }
    }
  }

  componentDidMount() {
    requestApi.fetchData('discover', 'movie').then(response => {
      this.setState({ data: response.data, isLoading: false });
    });
  }

  getMoviebyId = (id) => {
    requestApi.fetchDataById('movie', id).then(response => {
      this.props.data(response.data);
    });
  };

  nextPage = (e) => {
    const page = this.state.data.page + 1;
    requestApi.fetchDataPaginate('discover', 'movie', page).then(response => {
      this.setState({ data: response.data });
    });
  };
  prevPaginate = (e) => {
    const page = this.state.data.page - 1;
    requestApi.fetchDataPaginate('discover', 'movie', page).then(response => {
      this.setState({ data: response.data });
    });
  };

  render() {
    return (
      <div className="container">
        <div className="ss_media">
          <h2 className="title">Discover New Movies & TV Shows</h2>
          <MovieList
            movie={this.getMoviebyId}
            routeProps={this.props}
            prevPaginate={this.prevPaginate}
            nextPaginate={this.nextPage}
            moviesList={this.state.data} />
        </div>
      </div>
    );
  }
}

export default Movies;
4

5 回答 5

2

您的 DiscoverPage 组件在路由更改时卸载,之后您调用 getDataItem 方法。这就是 react 抛出错误的原因。

我建议使用 react-router 组件方法而不是渲染并将数据移动到商店,然后任何组件都可以访问该商店。参考react-router 渲染方法

例子:

<Route path="/user/:username" component={User}/>
于 2018-06-26T09:46:25.367 回答
1

需要进行一些更改,您使用的逻辑效率
不如我从您的问题中理解的那样有效

import React, { Component } from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import TvShows from './TvShows';
import Movies from './Movies';
import MovieDetail from '../MoviePage/MovieDetail';
import TvDetail from '../TvShowPage/TvDetail';

class DiscoverPage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            data: {}
        };
    }
    getALLDATA() {
        //update your state after your request
        request().then(res => {
            this.setState({
                data: res.data
            });
        });
    }
    componentDidMount() {
        this.getALLDATA();
    }
    getDataItem = () => {
        return this.state.data;
    };

    render() {
        return (
            <Switch>
                <Route exact path="/discover/movie" render={props => <Movies data={this.getDataItem} {...props} />} />
                <Route path="/discover/tv" render={props => <TvShows data={this.getDataItem} {...props} />} />
                <Route path="/movie/:movie" render={props => <MovieDetail data={this.state.data} {...props} />} />
                <Route path="/tv/:tv" render={props => <TvDetail data={this.state.data} {...props} />} />
                <Redirect to="/discover/movie" />
            </Switch>
        );
    }
}

export default DiscoverPage;
于 2018-06-26T09:50:56.130 回答
1

您收到的警告是告诉您不能调用setState未安装的组件,即当前未呈现的组件。最有可能的是,您的其他组件之一(Movies,TvShows等)正在调用您作为data道具传递的方法。当这些组件之一被渲染时,该组件不被渲染,因此通过propDiscoveryPage调用将导致'方法在未渲染时被调用。为了解决这个问题(假设你想要做的是将数据从一个组件传递到另一个组件)我建议也许使用来存储一个共享变量(或者,正如@Anu 建议的那样,一些其他类型的存储)。getDataItemdataDiscoveryPagesetStatelocalStorage

我不明白你对“参数”的意思,但是,回答你的问题,你可以调用setState任何component...方法(即使调用它componentWillUnmount几乎没有意义,因为这个方法只在组件即将调用时调用被卸载)。

编辑:查看Movie组件。在方法localStorage中用作我们的商店,getMovieById而不是:

getMoviebyId = (id) => {
    requestApi.fetchDataById('movie', id).then(response => {
        //this next sentence will call DiscoveryPage's setState
        //DiscoveryPage is not mounted, so a warning will be raised
        this.props.data(response.data);
    });
};

你可以有类似的东西:

getMoviebyId = (id) => {
    requestApi.fetchDataById('movie', id).then(response => {
        //any component can access this store
        localStorage.setItem("data", response.data);
    });
};

然后,在其他一些组件或方法中,像这样访问它:

localStorage.getItem("data");

你的代码有点混乱。我在理解您在哪里使用DiscoveryPage's时遇到了一些麻烦data,这是您在调用该data方法时设置的那个。但无论如何,希望这会有所帮助。

再补充一点:使用时localStorage,管理它是一种好习惯,我的意思是localStorage.removeItem("data")在您使用完该data值后调用。例如,最好通过某种componentWillUnmount方法来做到这一点。

于 2018-06-26T09:53:25.600 回答
0

我认为您缺少从中导入BrowserRouterreact-router-dom

import { Switch, Route, Redirect, BrowserRouter } from 'react-router-dom'; // Add BrowserRouter

class DiscoverPage extends Component {

...
render() {
    return (
    <BrowserRouter> // Add this 
        <Switch>
            <Route exact path="/discover/movie" render={props => <Movies data={this.getDataItem} {...props} />} />
            <Route path="/discover/tv" render={props => <TvShows data={this.getDataItem} {...props} />} />
            <Route path="/movie/:movie" render={props => <MovieDetail data={this.state.data} {...props} />} />
            <Route path="/tv/:tv" render={props => <TvDetail data={this.state.data} {...props} />} />
            <Redirect to="/discover/movie" />
        </Switch>
    </BrowserRouter>
    );
}
}
于 2018-06-26T09:37:16.070 回答
0

我们可以在componentDidMount中设置状态,例如:

componentDidMount(){
    var {state, setState} = this.props;
    setState({doNotRender: true});
}

但改为使用componentDidUpdate,例如:

componentDidUpdate(){
    var {state, setState} = this.props;
    if(!state.doNotRender){ //any_condition ur choice
       setState({doNotRender: true});
    }
}
于 2018-06-26T09:45:19.170 回答