react在切换路由时报错

报错信息为

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.

代码是这样的:

import React ,{Component} from 'react';
import { render } from 'react-dom';
import {IndexRoute,browserHistory, Router, Route, Link } from 'react-router';
import axios from 'axios';
import $ from 'jquery';
import App from './components';

function jianjie(){
    return (
        <h3>jianjie</h3>
    )
}

class yewu extends Component{
    constructor(props){
        super(props)
        this.state = {
            loading: true,
            error: null,
            data: null
        }
    }
    componentDidMount() {
        $.get('https://api.github.com/search/repositories?q=javascript&sort=stars').then(
            value => this.setState({loading: false, data: value}),
            error => this.setState({loading: false, error: error}));
    }
    render(){
        if (this.state.loading) {
            return <span>Loading...</span>;
        }
        else if (this.state.error !== null) {
            return <span>Error: {this.state.error.message}</span>;
        }
        else {
            var repos = this.state.data.items;
            var repoList = repos.map(function (repo, index) {
                return (
                    <li key={index}><a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description}</li>
                );
            });
            return (
                <div>
                    <h1>Most Popular JavaScript Projects in Github</h1>
                    <ol>{repoList}</ol>
                </div>
            );
        }
    }
}

function yewu2(){
    return <yewu promise={'https://api.github.com/search/repositories?q=javascript&sort=stars'}/>
}



function lianxi(){
    return (
        <h3>lianxi</h3>
    )
}
function test(){
    return (
        <h3>test</h3>
    )
}
function About(){
    return (
        <h3>About</h3>
    )
}

function Home(){
    return (
        <h3>Home</h3>
    )
}

const ele = document.createElement('div');
document.body.appendChild(ele);
const props = {
    name:"clm",
    age:30
};
render(
    <Router history = {browserHistory} >
    <route path ="/" component={App} >
        <IndexRoute  component={Home} />
        <route path ="/jianjie" component={jianjie} />
        <route path ="/yewu" component={yewu} />
        <route path ="/lianxi" component={lianxi} />
        <route path ="/about" component={About} />
        <route path ="/test" component={test} />
    </route>
</Router>, ele)

主要是这一部分:

class yewu extends Component{
    constructor(props){
        super(props)
        this.state = {
            loading: true,
            error: null,
            data: null
        }
    }
    componentDidMount() {
        $.get('https://api.github.com/search/repositories?q=javascript&sort=stars').then(
            value => this.setState({loading: false, data: value}),
            error => this.setState({loading: false, error: error}));
    }
    render(){
        if (this.state.loading) {
            return <span>Loading...</span>;
        }
        else if (this.state.error !== null) {
            return <span>Error: {this.state.error.message}</span>;
        }
        else {
            var repos = this.state.data.items;
            var repoList = repos.map(function (repo, index) {
                return (
                    <li key={index}><a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description}</li>
                );
            });
            return (
                <div>
                    <h1>Most Popular JavaScript Projects in Github</h1>
                    <ol>{repoList}</ol>
                </div>
            );
        }
    }
}

yewu组件的componentDidMount里面有个ajax,并且在回调里面调用this.setState()

再看路由部分代码:

<Router history = {browserHistory} >
    <route path ="/" component={App} >
        <IndexRoute  component={Home} />
        <route path ="/jianjie" component={jianjie} />
        <route path ="/yewu" component={yewu} />
        <route path ="/lianxi" component={lianxi} />
        <route path ="/about" component={About} />
        <route path ="/test" component={test} />
    </route>
</Router>

当路由切换到/yewucomponentDidMount里面的回调函数执行完成后再切换别的路由不会报错,但是如果回调没有执行,切换别的路由会报错,

在别的网站看到这样一条信息:

估计是你setState在异步的callback里执行,而这个时候由于你返回上一页,组件已经被unmount了。可以考虑在unmount的时候取消相关pendingRequest的回调,比如ajax请求的话,就abort掉。或者用isMounted183做下判断,不过根据文档,这个api可能日后被移除。

但是还是组织到怎么修正代码;

阅读 4.9k
4 个回答

构造方法里修改this.state有问题吧,应该用getInitialState,或者写在componentDidMount里面

如果你调用了外部的一些代码库,在好都是在componentWillUnmount这个周期中把引用的组件detroy,一般库都会有这个方法的

好吧,那我在改一改代码
修改后的代码:
1、组件名开头字母请大写!
2、在componentWillMount设置state初始状态,当然,在didMount也行。
3、修改ajax。

class Yewu extends Component{
    constructor(props){
        super(props)
    }
    componentWillMount() {
        this.setState({
            loading: true,
            error: null,
            data: null
        })
    }
    componentDidMount() {
        $.get('https://api.github.com/search/repositories?q=javascript&sort=stars')
        .then( value => 
            this.setState({loading: false, data: value})
            )
        .catch(error => 
            this.setState({loading: false, error: error}))
            )
    }
    render(){
        if (this.state.loading) {
            return <span>Loading...</span>;
        }
        else if (this.state.error !== null) {
            return <span>Error: {this.state.error.message}</span>;
        }
        else {
            var repos = this.state.data.items;
            var repoList = repos.map(function (repo, index) {
                return (
                    <li key={index}><a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description}</li>
                );
            });
            return (
                <div>
                    <h1>Most Popular JavaScript Projects in Github</h1>
                    <ol>{repoList}</ol>
                </div>
            );
        }
    }
}

clipboard.png

我点击招聘时,招聘组件里面发送了一个ajax请求,但是在这个请求没有回来前,或者说回调没有执行前,我又去点击问答了,此时就会报错啊,报的就是上面的错啊,
这里有一个老土的解决方法,就是在构造函数定义一个私有变量 this.aa 反正随便叫个什么;

发送ajax时 this.aa = ajax({...});

当组件卸载时;要做一件重要的事情,清除刚才发出的还没有相应的回调要这么干:

componentWillUnmount(){
        console.log("222")
        this.aa.abort();
    }

但是这是用jquery的ajax;当然问题到这已经解决了,但是咱们不妨深入思考下,如果,发送ajax请求返回的不是XHR而是一个Promise,那么用什么方法可以组织这个Promise的执行呢;
这就是上面的问题了,希望大家给个解决方案啊,基本就是在componentWillUnmount里面终端promise的回调

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题