问题
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"));
here is 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
回答1:
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);
}
回答2:
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.state = { navlist: [] };
}
componentDidMount() {
$.get('api/call')
.then(( response ) => this.setState({ navlist: response } ));
}
render() {
return (
<div>
<Header />
<SideBar navlist={this.state.navlist}/>
<HomeContent />
</div>
);
}
};
ReactDOM.render(<App/>, document.getElementById("app"));
回答3:
Note: I have added a timeout of 2 seconds to simulate API call. so results will be seen after 2 seconds
const jsonResponse = {
header: {
title: "CCP",
description: "",
error: true,
message:
"Method getMenu Not Implemented in CCP\\Controller\\UserController",
user: "UddinS2",
nav: {
container: [
{
title: "Users",
link: "#",
theme: "#007c92",
sub_modules: [
{
title: "Getting Started",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "File - CM Template v6.3",
link: "/library/templates/FW-LB-CommsMatrix-Template-v6.3.xls"
},
{
title: "File - Request Access",
link: "/library/docs/Requesting_Access_to_CCP.pdf"
},
{
title: "Video - CCP Promo",
link: "/video_ccp.html"
},
{
title: "Video - CCP Demo",
link: "/video_ccp_demo.html"
},
{
title: "Video - Request Access",
link: "/video_request_access.html"
},
{
title: "Video Tutorials",
link: "/video_tutorials.php"
}
]
},
{
title: "General",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "DC Standard Networks",
link: "/api/user/dcsn/format/xml"
},
{
title: "Find Comms Matrix Flow",
link: "/find_comms_matrix_flow.php"
},
{
title: "Find Host",
link: "/find_host.php"
},
{
title: "Find IP Network",
link: "/find_network.php"
},
{
title: "List Comms Matrices",
link: "/comms_matrices.php"
},
{
title: "My Exemption Requests",
link: "/api/user/exemption/requests/format/xml"
},
{
title: "Request Exemption",
link: "/request_exemption.php"
},
{
title: "Services, Projects, ...",
link: "/api/user/services/format/xml"
},
{
title: "View FW Objects",
link: "/view_network_service_objects.php"
},
{
title: "Create Comms Matrix",
link: "/create_comms_matrix.php"
},
{
title: "Comms Matrix Templates",
link: "/api/user/commsmatrix/templates"
},
{
title: "Upload Comms Matrix",
link: "/upload_comms_matrix.php"
},
{
title: "Verify Policy Flow",
link: "/verify_policy_flow.php"
},
{
title: "View Security Policy",
link: "/security_zoning_policy.php"
},
{
title: "View Routing Policy",
link: "/routing_domain_policy.php"
}
]
},
{
title: "IP Management",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Adressing Types",
link: "/api/user/iam/addressing/types/format/xml"
},
{
title: "Cramer Ranges (Live)",
link: "/api/user/iam/cramer/ranges/format/xml"
},
{
title: "Cramer Spool Queue",
link: "/api/user/cramer/spool/queue/format/xml"
},
{
title: "Create Subnet",
link: "/iam_create_subnet.php"
},
{
title: "Subnet Types",
link: "/api/user/iam/subnet/types/format/xml"
},
{
title: "Zone Type Sizes",
link: "/api/user/security/zone/type/sizes/format/xml"
},
{
title: "My Network Diagrams",
link: "/api/user/iam/network/diagrams/format/xml"
},
{
title: "My Subnet Requests",
link: "/api/user/subnet/requests/format/xml"
},
{
title: "Range Assignments",
link: "/api/user/iam/range/assignments/format/xml"
},
{
title: "Upload Network Diagram",
link: "/iam_upload_diagram.php"
}
]
},
{
title: "Networks",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Entity Networks",
link: "/api/user/networks/by/entity/format/xml"
},
{
title: "Frozen Objects",
link: "/api/user/frozen/objects/format/xml"
},
{
title: "IPBB Metadata",
link: "/api/user/ipbb/metadata/format/xml"
},
{
title: "Map Routing Zones",
link:
"/api/user/routing/domains/mapping/routingzone/format/xml"
},
{
title: "NITX Trace",
link: "/nitx_trace.php"
},
{
title: "Routing Domains",
link: "/api/user/routing/domains/format/xml"
},
{
title: "Routing Domain Aliases",
link: "/api/user/aliases/routing/domains/format/xml"
},
{
title: "Routing Domains ACL",
link: "/api/user/routing/domains/acl/format/xml"
},
{
title: "Routing Exemptions",
link: "/api/user/routing/exemptions/format/xml"
}
]
},
{
title: "Reporting",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Statistics Dashboard",
link: "/statistics_dashboard.php"
},
{
title: "ECT Statistics",
link: "/api/user/connectivity/test/statistics"
},
{
title: "Exemption Requests",
link: "/api/user/report/exemption/requests/format/xml"
},
{
title: "Exemption Policies",
link: "/api/user/report/exemption/policies/format/xml"
}
]
},
{
title: "Security",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Firewall Network Objects",
link: "/firewall_network_objects.php"
},
{
title: "Firewall Service Objects",
link: "/firewall_service_objects.php"
},
{
title: "Local Market Networks",
link: "/api/user/networks/opco/format/xml"
},
{
title: "Security Exemptions",
link: "/api/user/exemptions/format/xml"
},
{
title: "Security Zones",
link: "/api/user/security/zones/format/xml"
},
{
title: "Security Zone Aliases",
link: "/api/user/aliases/security/zones/format/xml"
},
{
title: "Network Zone Aliases",
link: "/api/user/aliases/network/zones/format/xml"
},
{
title: "Map Security Zones",
link: "/api/user/security/zones/mapping/seczone/format/xml"
},
{
title: "Map Network Zones",
link: "/api/user/security/zones/mapping/netzone/format/xml"
},
{
title: "Security Zones ACL",
link: "/api/user/security/zones/acl/format/xml"
}
]
},
{
title: "Audit",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Firewall Network Objects",
link: "/api/user/audit/firewall/network/objects/format/xml"
},
{
title: "Firewall Service Objects",
link: "/api/user/audit/firewall/service/objects/format/xml"
},
{
title: "Security Zones",
link: "/api/user/audit/security/zones/format/xml"
},
{
title: "Security Zones ACL",
link: "/api/user/audit/security/zones/acl/format/xml"
},
{
title: "Security Zones Aliases",
link: "/api/user/audit/security/zone/aliases/format/xml"
},
{
title: "Security Zone Mappings",
link: "/api/user/audit/security/zone/mapping/format/xml"
},
{
title: "Network Zone Aliases",
link: "/api/user/audit/network/zone/aliases/format/xml"
},
{
title: "Routing Domains",
link: "/api/user/audit/routing/domains/format/xml"
},
{
title: "Routing Domains ACL",
link: "/api/user/audit/routing/domains/acl/format/xml"
},
{
title: "Routing Domain Aliases",
link: "/api/user/audit/routing/domain/aliases/format/xml"
},
{
title: "Routing Domain Mappings",
link: "/api/user/audit/routing/domain/mapping/format/xml"
}
]
}
]
},
{
title: "Site Administration",
link: "#",
theme: "#007c92",
sub_modules: [
{
title: "Content Management",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Business Units",
link: "/api/user/businessunits/format/xml"
},
{
title: "Entities",
link: "/api/user/entities/format/xml"
},
{
title: "Locations",
link: "/api/user/locations/format/xml"
},
{
title: "Markets",
link: "/api/user/markets/format/xml"
},
{
title: "Service Domains",
link: "/api/user/servicedomains/format/xml"
}
]
},
{
title: "Health Check",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Networks Status",
link: "/api/user/networks/status/format/xml"
}
]
},
{
title: "User Management",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Assign Group Privilges",
link: "/assign_group_privs.php"
}
]
},
{
title: "Troubleshooting",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Debug Comms Matrix",
link: "/debug_comms_matrix.php"
},
{
title: "Mocha Tests",
link: "/testrunner.php"
}
]
},
{
title: "Super Admin",
link: "#",
theme: "#007c92",
menuitem: [
{
title: "Sudo Group Access",
link: "/sudo_group_access.php"
}
]
}
]
}
]
},
server: 1,
version: "17.1",
timestamp: 1514902917
},
body: []
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
navList: []
};
}
componentDidMount() {
//$.get('/api/user/get/user/method/menu/format/json')
//.done ((response) => console.log(response));
//.done(( response ) => this.setState({ navlist: //response } ));
//dont have actual api hence setting state with response data mentioned in question
setTimeout(() => {
this.setState({
navList: jsonResponse
});
}, 2000);
}
componentWillUnmount() {}
render() {
//removing header and HomeContent as definition is not there
return (
<div>
<SideBar navlist={this.state.navList} />{" "}
</div>
);
}
}
let SideBar = props => {
const { navlist } = props;
let navlistView = null;
const getMenuItems = menuItemsArray => (
<ul>
{" "}
{menuItemsArray.map((element, index) => (
<li key={index}>{element.title} </li>
))}
</ul>
);
const getSubMenu = subMenuArray => (
<div>
{" "}
{subMenuArray.map((element, index) => (
<ul key={index}>-- - {element.title}
{ <li>
{getMenuItems(element.menuitem)}
</li>
}
</ul>
))}
</div>
);
const getView = () => {
let navlistView = null;
if (navlist.header) {
const navInfo = navlist.header.nav.container;
navlistView = navInfo.map((navlist, index) => (
<div key={index}>
{navlist.title}
<div>
{getSubMenu(navlist.sub_modules)}
</div>
</div>
));
}
return navlistView;
};
return (
<div id="sidebar-wrapper" className="hidden-print">
{" "}
{getView()}{" "}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="app"></div>
For first error
Failed! TypeError: Cannot read property 'setState' of undefined
issue : callback doesnot have correct context.
Failed! TypeError: Cannot read property 'menuList' of undefined
This is also context issue as getJson is called with new context.
I am working on linux os , dont have IE , So I haven't checked on IE. i have used index as the keys because i don't have have any unique id. Please check what unique id you can set as keys.
回答4:
You have a setState
in your componentDidMount
. That creates a loop where your component will keep mounting itself forever (since setState
eventually leads to componentDidMount
being called), which is why React prevents it. The data you get through getJSON
should only update the state, not be passed on to one of React's rendering phases. When the state is updated, then your component should re-render automatically. Most of the logic you have in componentDidMount
should be in your render
function, formatting the data so it can then be used by the return
command below.
回答5:
You will try this
fetch(url)
.then((response) => {
let jsonData=response.json();
this.setState({data:jsonData})
})
.catch((error) => {
console.log("error---",error);
})
回答6:
I would move the logic of your get request into componentWillMount. The React error is telling you that the promise can only be called in a "Mounting component". Then you can call setState. Then in your render you can conditionally render based on the state variable.
{this.state.navList.length &&
<div>
<Header />
<SideBar navlist={this.state.navlist}/>
<HomeContent />
</div>}
You can also render a different loading component while you wait for the data to return.
回答7:
seems like the issue was separating the code properly in different files. I had index
, App
with componentDidMount()
all in one which was not working.
so i have done
index
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { BrowserRouter, Route, browserHistory } from 'react-router-dom';
import promise from 'redux-promise';
import App from './App'
import reducers from './reducers';
require("babel-core/register");
require("babel-polyfill");
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter history={browserHistory}>
<App/>
</BrowserRouter>
</Provider>
, document.getElementById('root'));
App
import React, { Component } from 'react'
import { HashRouter, Switch, Route, Link } from 'react-router-dom';
import Header from './components/header';
import Logout from './components/logout';
import SideBar from './components/sidebar';
import HomeContent from './components/home';
import Ldapuser from './components/ldapuser';
import AdminContent from './components/getting_started/admin';
const Main = () => (
<main>
<Switch>
<Route exact path='/index.html' component={HomeContent}/>
<Route exact path='/home' component={HomeContent}/>
<Route path='/logout' component={Logout}/>
<Route path='/ldapuser' component={Ldapuser}/>
<Route path='/admin/content' component={AdminContent}/>
</Switch>
</main>
)
class App extends Component {
render() {
return (
<div>
<Header />
<SideBar />
<Main />
</div>
);
}
}
export default App;
and the individual files... header
etc will have componentDidMount()
which now works
来源:https://stackoverflow.com/questions/48062682/react-warning-cannot-set-state-when-using-promises