问题
What I am trying to do: I am trying to practice MERN structure, so I wanted to set up a node.js server and react front-end first.
However, what happened is that when the server gets fired, the entire DOM got overwrite, why is that?
server.js is a simple node.js
const express = require('express');
const app = express();
// console.log that your server is up and running
app.listen(3000, () => console.log(`Listening on port 3000`));
// create a GET route
app.get('/test', (req, res) => {
res.send({ express: 'I MADE IT' });
});
React.js is my front-end:
import React from 'react';
import logo from './img/nav-logo.png';
import Product1 from './img/manage.png';
import Product2 from './img/deliver.png';
import Product3 from './img/market.png';
import Service from './service.js'
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
class App extends React.Component {
constructor(props){
super(props)
this.state={
items: [{
desc:"Manage",
price: 5000,
purchased: false,
},
{
desc:"Deliver",
price: 2000,
purchased: false,
},
{
desc:"Market",
price: 4000,
purchased: false,
}
],
data:null,
}
}
componentDidMount() {
console.log("here")
this.callApi()
.then(res => this.setState({ data: res.express }))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/test');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
handleClick(desc){
const newItems = this.state.items.slice()
this.printItems(newItems)
for(var item of newItems){
if(item.desc === desc){
item.purchased = !item.purchased
}
}
this.printItems(newItems)
this.setState({
items: newItems
})
}
printItems(items){
console.log(items[0].purchased + " " + items[1].purchased + " " + items[2].purchased)
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to Shopping Center</h1>
</header>
<div>
<div>
{this.state.data} //<------------trying to render the data here
</div>
<Service url={Product1} desc="Manage" alt="MA" purchased={this.state.items[0].purchased} thisClick={ (desc) => this.handleClick("Manage")} />
<Service url={Product2} desc="Deliver" alt="DE" purchased={this.state.items[1].purchased} thisClick={ (desc) => this.handleClick("Deliver")}/>
<Service url={Product3} desc="Market" alt="MR" purchased={this.state.items[2].purchased} thisClick={ (desc) => this.handleClick("Market")}/>
</div>
</div>
);
}
}
export default App;
Without my node.js running, i can render my react just fine.
However, once I do npm server.js. localhost:3000 will no longer work. So i treid localhost:3000/test, and the entire HTML became the string "{ express: 'I MADE IT' }"
My index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
I want to render the message as part of my regular React DOM, but somehow the node server overwritten my entire front-end.
Additionally, i got an error message in console state the following:
SyntaxError: Unexpected token < in JSON at position 0
at App._callee$ (App.js:42)
at tryCatch (runtime.js:62)
at Generator.invoke [as _invoke] (runtime.js:296)
at Generator.prototype.(:3000/anonymous function) [as next] (http://localhost:3000/static/js/bundle.js:31252:21)
at step (App.css?9a66:26)
at App.css?9a66:26
The line of code that I located is the componentDidMount() method
My package.json file:
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"bootstrap": "^4.1.3",
"express": "^4.16.3",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-scripts": "1.1.5",
"reactstrap": "^6.4.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:3001"
}
回答1:
For development, given you are using create-react-app for your React application, you can use a development proxy to run both Express and React on separate ports. Any fetch() requests will be redirected to the specified proxy path/port in package.json. This will allow you take advantage of development tools for each platform.
- Change the Express server port to anything other than
3000. For exampleapp.listen(3001, () => console.log('Listening on port 3001'));This is so create-react-app can run on3000and Express can run on3001. - Added the following line to the React application's
package.jsonto enable a proxy,"proxy": "http://localhost:3001". A fetch request to/api/testwill be redirect tohttp://localhost:3001/api/test.
For production, given you would want both applications to run on the same port:
- Set up static resource loading for the server using static() method.
- Added logic to server to redirect all requests that do not match you "API" paths, to load the
index.htmlof the production builtnpm run buildReact application using sendFile(). This will allow to use routing within the React application.
Here is what that could look like at a basic level, with the create-react-app build folder generated from npm run build command is placed into path /public/build:
app.use(express.static(path.join(__dirname, 'public', 'build')));
// API route
app.get('/api/test', (req, res) => {
res.send({ express: 'I MADE IT' });
});
// catch-all route
app.use('*', function (request, response) {
response.sendFile(path.join(__dirname, 'public', 'build', 'index.html'));
});
React fetch():
// ...
const response = await fetch('/api/test'); // this matches the path specified on server, before the catch all route
const body = await response.json();
// ...
Check out this answer as it was a similar issue.
Update: - Steps to create simple Express + React development environment:
- Create Express application using express-generator. Run command
npx express-generator react-express - Navigate into created project. Run command
cd react-express. - Run command
npm install. Then delete directorypublic. - Create React application using
create-react-app. Run commandnpx create-react-app public. - Install
concurrently. Run commandnpm install concurrently --save-dev. - Edit
app.jsat the base of project created byexpress-generator. Add the following lines starting at line 25. This is to expose and endpoint at/api/testand provide a "catch-all" route to loadindex.htmlgenerated bynpm run build.
```
app.get('/api/test', (req, res) => {
res.send({ express: 'I MADE IT' });
});
app.use('*', function (request, response) {
response.sendFile(path.join(__dirname, 'public', 'build', 'index.html'));
});
```
- Edit
/bin/wwwline 15 to change port from 3000 to 3001.var port = normalizePort(process.env.PORT || '3001'); - Edit
package.jsoncreated by create-react-app at/build/package.json. Add the following line"proxy": "http://localhost:3001". - At the base
package.jsonlocated at the root of the project, created by express-generator, add the following line to srcipts:"dev": "concurrently \"node ./bin/www\" \"cd public && npm start\"" - Run command
npm run devfrom the base of the project. This will load both the server (Express) and client (React) and will proxy calls, in this example/api/teston port3000will be directed to the server port running at3001.
Hopefully that helps!
来源:https://stackoverflow.com/questions/52521509/node-js-server-overwritten-entire-dom-with-react-js