Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
260 views
in Technique[技术] by (71.8m points)

javascript - react warning cannot set state when using promises

I'm trying to query the server to get list of nav items so I can build my menu on init. I've so far managed to create a static page with 3 contents on the home page, which includes header, sidebar and content. The sidebar is the menu which is different for different type of users so I need to retrieve the menu items on load.

Error I receive is

Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op

Update 4

so i moved my api requests to index.js file and tried to add the condition as mentioned in the comment. Now it just renders Loading... with the same error

    import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import 'core-js/fn/promise';

import SideBar from './components/sidebar';
import Header from './components/header';
import HomeContent from './components/home';


function getJSON(url) {
  return get(url).then(JSON.parse);
}

function get(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
        // Resolve the promise with the response text
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
    req.send();
  });
}

class App extends Component {
    constructor(props){
        super(props);
        this.state = {
                        username: '',
                        user,
                        loading1: true ,
                        menuList : [],
                        loading2: true
        };
    }

    componentDidMount() {
        let currentComponent = this;

        getJSON('/api/user/get/user/method/user/format/json?quiet=1').then((response) => {
            //console.log(JSON.stringify(response));
            //currentComponent.state.username = response.body.recordset.record[0].name;
            //currentComponent.state.user = response.body.recordset.record[0];
            currentComponent.setState({
                username: response.body.recordset.record[0].name,
                loading1: false
            });
        }).catch(error => {
            console.log('Error', error);
        });

        getJSON('/api/user/get/user/method/menu/format/json?quiet=1').then((response) => {
            console.log(JSON.stringify(response));
            let menuData = response.body.recordset.record;
            let menuList = [];
            var i = 0;
            menuData.container.forEach(menus => {
                menus.sub_modules.forEach(submenu => {
                    menuList.push(<li className="menu" key={i}>{ submenu.title }</li>);
                    i++
                    if (submenu.menuitem.length) {
                        submenu.menuitem.forEach(menuitem => {
                            menuList.push(<li key={i}><a href={menuitem.link}>{ menuitem.title }</a></li>);
                            i++;
                        })
                    }
                })
            });
            currentComponent.setState({
                menuList: menuList,
                loading2: false
            });
        }).catch(error => {
            console.log("Failed!", error);
            $('#myModalError .modal-body').html(error);
            $('#myModalError').modal('show');
        });
    }

    componentWillUnmount() {}

    render() {
        let content = '';
        console.log(this.state);
        if(this.state.loading1 || this.state.loading2) {
            content = <div>Loading...</div>
        }else{
            content =
                <div id="wrapper" className="toggled">
                    <Header username={this.state.username}/>
                    <SideBar menuList={this.state.menuList}/>
                    <HomeContent />
                </div>

        }
        return <div>
                   {content}
               </div>
    }
};

ReactDOM.render(<App/>,  document.getElementById("app"));

enter image description here

here is IE error

IE console error

detailed ie error

sidebar.js

import React, { Component } from 'react';

let menuList = [];
class SideBar extends Component {
    constructor(props){
        super(props);
    }

    render () {
        return (
            <div id="sidebar-wrapper" className="hidden-print">
                <ul className="sidebar-nav">
                {this.props.menuList}
                </ul>
            </div>
        );
    }
}

export default SideBar;

I have created a jsfiddle

webpack

var HTMLWebpackPlugin = require('html-webpack-plugin');
var HTMLWebpackPluginConfig = new HTMLWebpackPlugin({
    template: __dirname + '/app/index.html',
    filename: 'index.html',
    inject: 'body'
});

const ExtractTextPlugin = require("extract-text-webpack-plugin");

const extractSass = new ExtractTextPlugin({
    filename: "[name].[contenthash].css",
    disable: process.env.NODE_ENV === "development"
});

module.exports = {
    entry : [__dirname + '/app/index.js'],
    module: {
         rules: [
            {
                test: /.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader'
                }
            } ,
            {
                test: /.scss$/,
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader"
                }, {
                    loader: "sass-loader",
                }]
            }
        ],
    },
    output: {
        filename: 'transformed.js',
        path: __dirname + '/docs'
    },
    plugins: [
        HTMLWebpackPluginConfig
    ]
}


{
  "name": "ccp-react",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "start": "webpack-dev-server",
    "build": "webpack"
  },
  "private": true,
  "dependencies": {
    "babel-helper-bindify-decorators": "^6.24.1",
    "babel-plugin-lodash": "^3.3.2",
    "babel-plugin-syntax-dynamic-import": "^6.18.0",
    "babel-plugin-syntax-flow": "^6.18.0",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-plugin-transform-decorators": "^6.24.1",
    "babel-plugin-transform-react-display-name": "^6.25.0",
    "babel-plugin-transform-react-jsx": "^6.24.1",
    "babel-polyfill": "^6.26.0",
    "babel-preset-env": "^1.6.1",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-es2016": "^6.24.1",
    "babel-preset-es2017": "^6.24.1",
    "babel-preset-flow": "^6.23.0",
    "babel-preset-react-optimize": "^1.0.1",
    "babel-preset-stage-1": "^6.24.1",
    "babel-preset-stage-3": "^6.24.1",
    "bootstrap": "^3.3.7",
    "classnames": "^2.2.5",
    "commander": "^2.12.2",
    "create-react-class": "^15.6.2",
    "cross-env": "^5.1.3",
    "gulp": "^3.9.1",
    "hammerjs": "^2.0.8",
    "lodash": "^4.17.4",
    "ng": "0.0.0-rc6",
    "ng-cli": "^0.7.0",
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "react-icons-kit": "^1.0.7",
    "react-redux": "^5.0.6",
    "react-router": "^4.2.0",
    "react-router-dom": "^4.2.2",
    "react-side-bar": "^0.3.5",
    "react-sidenav": "^2.1.2",
    "redux": "^3.7.2",
    "rxjs": "^5.5.6",
    "systemjs": "^0.20.19",
    "web-animations-js": "^2.3.1",
    "zone.js": "^0.8.19"
  },
  "devDependencies": {
    "@types/jasmine": "~2.5.53",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "^6.0.95",
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-react": "^6.24.1",
    "core-js": "^2.5.3",
    "css-loader": "^0.28.8",
    "extract-text-webpack-plugin": "^3.0.2",
    "html-webpack-plugin": "^2.30.1",
    "jasmine-core": "~2.6.2",
    "jasmine-spec-reporter": "~4.1.0",
    "karma": "~1.7.0",
    "karma-chrome-launcher": "~2.1.1",
    "karma-cli": "~1.0.1",
    "karma-coverage-istanbul-reporter": "^1.3.3",
    "karma-jasmine": "^1.1.1",
    "karma-jasmine-html-reporter": "^0.2.2",
    "node-sass": "^4.7.2",
    "protractor": "~5.1.2",
    "sass-loader": "^6.0.6",
    "style-loader": "^0.19.1",
    "ts-node": "~3.2.0",
    "tslint": "~5.7.0",
    "typescript": "~2.4.2",
    "webpack": "^3.10.0",
    "webpack-bundle-analyzer": "^2.8.2",
    "webpack-dev-server": "^2.9.7"
  }
}

setstate issue still exists even with same code as the one in the fiddle. the fiddle works

setstate

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

try to check if the componenet mounted before you update:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import SideBar from './components/sidebar';
import Header from './components/header';
import HomeContent from './components/home';

class App extends Component {
    constructor(props){
        super(props);
        this.mountCheck = false;
        this.state = { navlist: [] };
    }

    componentWillMount() {
        this.mountCheck = true;
    }
    componentWillUnmount() {
        this.mountCheck = false;
    }

    componentDidMount() {
         $.get('/api/user/get/user/method/menu/format/json')
        .done(( response ) => this.setState({ navlist: response } ));
    }

    render() {
        return (
            <div>
                <Header />
                <SideBar navlist={this.state.navlist}/>
                <HomeContent />
            </div>
        );
    }
};

ReactDOM.render(<App/>,  document.getElementById("app"));

also you can add shouldComponentUpdate to improve performance and reduce wastful rendering e.g:

shouldComponentUpdate(nextProps, nextState) {
    if (this.state.navlist !== nextState.navlist) {
      return true;
    }
    return false;
  }

see https://reactjs.org/docs/optimizing-performance.html

deep compare check for better performance results you can use it with isEqual's lodash :

shouldComponentUpdate(nextProps, nextState) {
        return !isEqual(this.state, nextState);
      }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...