React-routes express-server - javascript

I'm learning to work with the rect, when I try to follow the link, I get such errors:
My client.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import App from '../components/first_page/App';
import {Router, Route } from 'react-router';
import { browserHistory } from 'react-router';
class Main extends Component {
render(){
return (
<Router>
<Route history = {browserHistory}>
<Route path="/f" component={App}/>
</Route>
</Router>
);
}
}
render (<Main />, window.document.getElementById('app'))
App.js
import React, { Component } from 'react';
import Bootstrap from 'bootstrap/dist/css/bootstrap.css';
import { Button } from 'react-bootstrap';
class App extends Component{
render(){
return (
<div>
<Button>Start!</Button>
<Button>Login!</Button>
</div>
);
}
}
export default App
Server
var express = require('express');
var path = require('path');
var config = require('../webpack.config.js');
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var app = express()
var compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, {noInfo: true, publicPath: config.output.publicPath}));
app.use(webpackHotMiddleware(compiler));
app.use(express.static(path.resolve(__dirname, './dist')));
app.use('/', function (req, res) {
res.sendFile(path.resolve('client/index.html'));
});
var port = process.env.PORT || 4000;
app.listen(port, function(error) {
if (error) throw error;
console.log("Express server listening on port", port);
});
I tried to pass different tutorials, but always these mistakes are obtained, I will be happy with any help, thanks

Related

Keep getting error with simple ReactJS FORM posting to Express JS server

I am new to ReactJS and trying to find a way to connect my React front end to my Express js backend. To do this, I am just setting up a simple form that sends a POST request after I press a register button in a form.
The problem is I keep getting these error messages in the console.
These are the errors
POST http://localhost:3000/api/users 404 (Not Found)
Error: Request failed with status code 404
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.onloadend (xhr.js:66)
This is the react code
import Proj from './Proj'
import React, {Fragment, useState} from 'react'
import axios from 'axios';
export const Homepage = () => {
const [formData, setFormData] = useState({
name:'',
});
const { name } = formData;
const onChange = e =>
setFormData({...formData, [e.target.name]:e.target.value})
const onSubmit = async e => {
console.log('submitted')
e.preventDefault();
const newUser = {
name,
}
try {
const config = {
headers:{
'Content-Type':'application/json'
} }
const body = JSON.stringify(newUser)
const res = await axios.post('/api/users',body, config)
console.log(res)
} catch (err) {
console.error(err)
}
}
return (
<div className="Landing">
<Proj/>
<section>
<form onSubmit = {e=>onSubmit(e)}>
<input
type="text"
placeholder="Name"
name="name"
value = {name}
onChange = {e =>onChange(e)}
required />
<input type="submit" className="btn btn-primary" value="Register" />
<h1>Contact</h1>
</form>
</section>
</div>
)
}
export default Homepage
This App.js
import { Navbar } from './components/Navbar';
import Homepage from './components/Homepage';
import Projects from './components/Projects';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import './App.css';
function App() {
return (
<Router>
<div className="App">
<Navbar/>
</div>
<Route exact path="/" component={Homepage} />
<Switch>
<Route exact path="/Projects" component={Projects} />
</Switch>
</Router>
);
}
export default App;
This is my server side JS
const express = require ('express')
const path = require('path');
const app = express()
app.use(express.urlencoded({extended: false}));
app.use('/', require('./routes/api/users'))
// Serve static assets in production
if (process.env.NODE_ENV === 'production'){
app.use(express.static('client/build'))
app.get('*', (req,res)=>{
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
})
}
const PORT = 5000
app.listen(process.env.PORT || 5000)
and this is users.js in the api folder in routes
const express = require('express')
const router = express.Router();
router.post('/', async (req,res)=>
{
console.log('its working, and here is the data', req.body)
})
module.exports = router;
client side package.json
"proxy":"http://localhost:5000",
I would really appreciate it if someone can tell me what I am doing wrong or if there is a better way to do things.Thanks.
Instead of
app.use('/', require('./routes/api/users'))
type in
app.use('/api/users', require('./routes/api/users'))
You are posting currently to a route that does not exist.

SSR app but React Router Navigation on the client side doesn't fetch data and breaks the page

I've created a SSR React app that loads data on the server and sends it to the client as html. The problem kicks in after the initial server request has been served and i try to switch to a different navigation link. The url changes to the correct path but the page itself breaks with a TypeError: Cannot read property 'length' or 'map' of undefined. I believe the fetching somehow is not working on the client side, because if i turn off JavaScript from the browser everything works just fine.
The App has four routes, Home, Movies (needs to fetch data), TvShows (needs to fetch data) and PageNotFound. Again, the problem occurs when for example I open the Home page and try to switch to Movies. However, if i open Movies or TvShows first everything loads correctly because of the initial request being served by the server. Here is my file structure:
And here's the content of some of my files:
index.js (Server.js)
import "#babel/polyfill";
import express from "express";
import { applyMiddleware, createStore } from "redux";
import Routes from "./client/Routes";
import { matchRoutes } from "react-router-config";
import renderer from "./helpers/renderer.js";
import thunk from "redux-thunk";
import reducers from "./reducers";
const compression = require("compression");
const app = express();
app.use(compression());
app.use(express.static("public")); //treats the public(client side) directory as public, available to the outside world
// This is fired every time the server side receives a request
app.get("*", (req, res) => {
// Create a new Redux store instance
const store = createStore(reducers, {}, applyMiddleware(thunk));
const promises = matchRoutes(Routes, req.path)
.map(({ route }) => {
return route.loadData ? route.loadData(store) : null;
})
.map((promise) => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve);
});
}
});
Promise.all(promises).then(() => {
// Send the rendered page back to the client
// Grab the initial state from our Redux store
const context = {};
//const finalState = store.getState();
const content = renderer(req, store, context);
if (context.notFound) {
res.status(404);
}
res.send(content);
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT);
client.js
import "#babel/polyfill";
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { Provider } from "react-redux";
import { renderRoutes } from "react-router-config";
import Routes from "./Routes";
import reducers from "../reducers";
// Grab the state from a global variable injected into the server-generated HTML
const preloadedState = window.__PRELOADED_STATE__;
// Create Redux store with initial state
const store = createStore(reducers, preloadedState, applyMiddleware(thunk));
ReactDOM.hydrate(
<Provider store={store}>
<BrowserRouter>
<div>{renderRoutes(Routes)}</div>
</BrowserRouter>
</Provider>,
document.querySelector("#root")
);
renderer.js
import React from "react";
import serialize from "serialize-javascript";
import { renderToString } from "react-dom/server";
import { StaticRouter } from "react-router-dom";
import { Provider } from "react-redux";
import Routes from "../client/Routes";
import { renderRoutes } from "react-router-config";
import { Helmet } from "react-helmet";
// Render the component to a string
export default (req, store, context) => {
const html = renderToString(
<Provider store={store}>
<StaticRouter location={req.path} context={context}>
<div>{renderRoutes(Routes)}</div>
</StaticRouter>
</Provider>
);
const helmet = Helmet.renderStatic();
return `
<!doctype html>
<html>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
</head>
<body>
<div id="root">${html}</div>
<script>
// WARNING: See the following for security issues around embedding JSON in HTML:
// https://redux.js.org/recipes/server-rendering/#security-considerations
window.__PRELOADED_STATE__ = ${serialize(store.getState())}
</script>
<script src="/bundle.js"></script>
</body>
</html>
`;
};
Routes.js
import App from "./App";
import HomePage from "./pages/HomePage";
import MovieListPage from "./pages/MovieListPage";
import TvShowsPage from "./pages/TvShowsPage";
import NotFoundPage from "./pages/NotFoundPage";
//using spread operator for the components
//and loadData function(if available)
//because they are imported in object form now
export default [
{
...App, //no path added to App, meaning it will always be displayed on screen
routes: [
{
...HomePage,
path: "/",
exact: true,
},
{
...MovieListPage,
path: "/movies",
exact: true,
},
{
...TvShowsPage,
path: "/tvshows",
exact: true,
},
{
...NotFoundPage, //will be shown if react router can't match any of the defined routes
},
],
},
];
MovieListPage.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMovies } from "../../actions";
import { Helmet } from "react-helmet";
class MovieListPage extends Component {
// Have state ready for both Movies and TvShows link clicks/direct requests
componentDidMount() {
if (this.props.movies) return;
this.props.fetchMovies();
}
renderData() {
return this.props.movies.results.map((movie) => {
return (
<div
key={movie.id}
className="card text-center m-3"
style={{ width: "15rem" }}
>
<img
className="card-img-top"
alt="..."
src={this.dynamicUrl(movie)}
/>
<div className="card-body">
<h5 className="card-title">{movie.title}</h5>
<p className="card-text font-weight-light">{movie.release_date}</p>
<a href={this.dynamicLink(movie)} className="btn btn-secondary">
TMDB
</a>
</div>
</div>
);
});
}
dynamicUrl(movie) {
let url = "https://image.tmdb.org/t/p/w200/" + movie.poster_path;
return url;
}
dynamicLink(movie) {
let link = "https://www.themoviedb.org/movie/" + movie.id;
return link;
}
head() {
return (
<Helmet>
<title>{`${this.props.movies.results.length} Movies Loaded`}</title>
<meta property="og:title" content="Movies" />
</Helmet>
);
}
render() {
return (
<div className="container">
{this.head()}
<div className="row">{this.renderData()}</div>
</div>
);
}
}
function mapStateToProps(state) {
return { movies: state.movies };
}
function loadData(store) {
return store.dispatch(fetchMovies());
}
//exporting the component and the loadData function (if present)
//in the form of an object(key:value pair)
// to avoid overlap of different loadData function imports in Routes
export default {
loadData,
component: connect(mapStateToProps, { fetchMovies })(MovieListPage),
};
I can't seem to figure out what is it that i'm missing.
I found the issue in renderer.js. I didn't provide the correct path for my client-side bundle.js in the html served by the server. Instead of <script src="/bundle.js"></script> it had to be <script src="/public/bundle.js"></script>.

React not rendering on desktop safari or any mobile (iOS tested only) browser when page refresh or manual navigation

This seems like a duplicate of a few others. But no solution has worked for me.
Navigating via links works fine. Refreshing pages or manual navigating only works on desktop (chrome and firefox, not working on Safari).
On desktop safari, and all iOS browsers, it simply shows the entire JSON object in the browser and doesn't seem to be serving the static files.
I’ve tried Router, BrowserRouter and HashRouter. The only one that works is HashRouter. But, I don’t want hashes in the url.
I'm not getting any errors, and I've console logged all over.
When I placed a log in the getProducts action creator and on the server "/products" route, Safari doesn't show the action creator console log in the browser. But, heroku logs show that the path="/products" is being hit, but not the path="/static/css/main.etc.," or path="/static/js/main.etc.," files.
Things I've looked into and/or tried:
React-router urls don't work when refreshing or writting manually
Web page not rendering correctly in iOS browsers or desktop Safari
https://github.com/ReactTraining/react-router/issues/4727
How to remove the hash from the url in react-router
React Routing works in local machine but not Heroku
https://github.com/ReactTraining/react-router/issues/4671
Here's a stripped back sample. Note: I'm using concurrently to proxy my requests.
// client/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './styles/index.css';
import App from './App'
import registerServiceWorker from './registerServiceWorker'
import { Router } from 'react-router-dom'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import reduxThunk from 'redux-thunk'
import history from './history'
import reducers from './reducers'
const store = createStore(
reducers,
applyMiddleware(reduxThunk)
)
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<App />
</Router>
</Provider>
, document.getElementById('root'))
registerServiceWorker();
// client/history.js
import createHistory from 'history/createBrowserHistory'
export default createHistory()
// client/App.js
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom'
import Home from './components/home'
import Header from './components/header'
import Products from './components/products'
import './styles/App.css'
class App extends Component {
render() {
return (
<div>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/products" component={Products} />
<Route render={() => <p>Not found</p>} />
</Switch>
</div>
);
}
}
export default App;
// client/components/products.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import * as actions from '../actions'
// import ‘../polyfill’ // imported polyfil object from core-js when previously using Object.values below… same results either way…
class Products extends Component {
componentWillMount() {
this.props.getProducts()
}
renderProducts() {
/* const { products } = this.props
return Object.values(products).map((product) => {
return (
<li key={product.title}>
{product.title}
</li>
)
});*/
const productsArray = []
const { products } = this.props
for(let key in products) {
productsArray.push(<li key={products[key].title} >{products[key].title}</li>)
}
return productsArray
}
render() {
if(!this.props.products) {
return (
<div></div>
)
}
return (
<div>
<ul className="productListItemUl" >{this.renderProducts()}</ul>
</div>
)
}
}
const mapStateToProps = state => {
return { products: state.products.products }
}
export default connect(mapStateToProps, actions)(Products)
// actions/index.js
import axios from 'axios'
import {
GET_PRODUCTS
} from './types'
export function getProducts() {
return async function(dispatch) {
try {
const products = await axios.get('/products')
dispatch({ type: GET_PRODUCTS, payload: products.data })
} catch (err) {
console.log('redux thunk getProducts() action creator error')
console.log(err)
}
}
}
// server.js
"use strict";
require("babel-core")
const express = require('express');
const path = require('path');
const app = express();
const port = process.env.PORT || 3050;
const mongoUtil = require('./server/mongoUtil')
mongoUtil.connect()
const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();
app.use(jsonParser);
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.resolve(__dirname, 'client/build')));
}
let productsRoute = require('./server/routes/products');
app.use('/products', productsRoute)
app.get('*', function(request, response) {
response.sendFile(path.resolve(__dirname, 'client/build', 'index.html'));
});
app.listen(port, () => console.log(`Listening on port ${port}.`));

React : POST and GET not found

I'm following this tutorial which I adapt to React Router v4.
I have these files :
auth.js
const express = require('express');
const validator = require('validator');
const router = new express.Router();
function validateLoginForm(payload) {
const errors = {};
let isFormValid = true;
let message = '';
if (!payload || typeof payload.email !== 'string' || payload.email.trim().length === 0) {
isFormValid = false;
errors.email = 'Please provide your email address.';
}
if (!payload || typeof payload.password !== 'string' || payload.password.trim().length === 0) {
isFormValid = false;
errors.password = 'Please provide your password.';
}
if (!isFormValid) {
message = 'Check the form for errors.';
}
return {
success: isFormValid,
message,
errors
};
}
router.post('/login', (req, res) => {
console.log("lol");
const validationResult = validateLoginForm(req.body);
if (!validationResult.success) {
return res.status(400).json({
success: false,
message: validationResult.message,
errors: validationResult.errors
});
}
return res.status(200).end();
});
router.get('/login', (req, res) => {
console.log(req.url);
});
router.get('/', (req, res) => {
console.log(req.url);
console.log("lmao")
});
module.exports = router;
index.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const router = new express.Router();
// tell the app to look for static files in these directories
app.use(express.static('./server/static/'));
app.use(express.static('./client/dist/'));
app.use(bodyParser.urlencoded({extended:false}));
const authRoutes = require('./server/routes/auth');
app.use('/login', authRoutes);
// start the server
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000 or http://127.0.0.1:3000');
});
Base.jsx
import React from 'react';
import PropTypes from 'prop-types';
import { Link, NavLink } from 'react-router-dom';
const Base = ({ child }) => (
<div>
<div className="top-bar">
<div className="top-bar-left">
<NavLink to="/">React App</NavLink>
</div>
<div className="top-bar-right">
<Link to="/login">Log in</Link>
</div>
</div>
{child.render()}
</div>
);
Base.propTypes = {
child: PropTypes.object.isRequired
};
export default Base;
and app.jsx
import React from 'react';
import ReactDom from 'react-dom';
import injectTapEventPlugin from 'react-tap-event-plugin';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import {BrowserRouter, Switch, Route} from 'react-router-dom';
import Base from './components/Base.jsx';
import HomePage from './components/HomePage.jsx';
import LoginPag from './components/LoginPag.jsx';
// for MaterialUI to work properly
injectTapEventPlugin();
const TestLogin = (props) => {
return (<Base child={LoginPag}/>)
};
const TestBase = (props) => {
return(<Base child={HomePage}/>)
};
ReactDom.render((<BrowserRouter><MuiThemeProvider muiTheme={getMuiTheme()}>
<div>
<Switch>
<Route exact path="/" component={TestBase}/>
</Switch>
<Route exact path="/login" component={TestLogin}/>
</div>
</MuiThemeProvider>
</BrowserRouter>), document.getElementById('react-app'));
As you can see, I did a little "workaround" to have everything rendered nicely and it works. But it only works for Client-side routing.
I can't reload pages via f5 or refresh button, nor can I send form and get it through router.post(). It automatically results in a 404 not found.
I printed req.url in router.get('*') to see where the thing goes down and it appears that everywhere I go, the server still receives the address /. I believe the matter is with the <Link to> tag..
How can I fix this and get the server "follow" the client side routing ?
I'm using latest versions of Express, React, and React-Router. Thanks in advance
EDIT : Edited to take into account the remarks made by VivekN
Change your index.js file to the below one:-
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const router = new express.Router();
// tell the app to look for static files in these directories
app.use(express.static('./server/static/'));
app.use(express.static('./client/dist/'));
app.use(bodyParser.urlencoded({extended:false}));
const authRoutes = require('./server/routes/auth');
app.use('/', authRoutes);
// start the server
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000 or http://127.0.0.1:3000');
});
The problem with your code is that you had written when a request comes to your server which has /login in its path, then that should go inside auth.js file and inside that you should check for router.post('/') method.
Either this or you change the index.js file to be
app.use('/', authRoutes);

React Router is able to do the routing only if started from root url

I have a React app that also uses Redux and ReactRouter.
My problem is the following:
When I launch the app and go to the root url, I can normally navigate inside the app, and the routes in the navigation bar will be changing as I navigate.
However, if I type in the navigation bar any url other than the root, I get a weird error:
I don't really get how one can get such an error.
If I go to localhost:1337/ and then click on the item with the link /cars/1, everything will be fine and the component will successfully get rendered. If I type localhost:1337/cars/1 right away (or any other existing route) I get this error.
Here's how I initialize the react-router and define my routes:
index.js:
require('./style/style.css');
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import reduxPromise from 'redux-promise';
import routes from './routes';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(
reduxPromise
)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<Router history={browserHistory} routes={routes} />
</Provider>
, document.querySelector('.container.app')
);
routes.js:
import React from 'react';
import { Route, IndexRoute, Redirect } from 'react-router';
import App from './components/app';
import CarsIndex from './containers/cars-index';
import CarNew from './containers/car-new';
import CarShow from './containers/car-show';
import CarEdit from './containers/car-edit';
import SignIn from './containers/signin';
import auth from './auth/auth';
function requireAuth(nextState, replace) {
if (!auth.loggedIn()) {
replace({
pathname: '/authenticate',
state: { nextPathname: nextState.location.pathname }
});
}
}
function filterLoggedIn(nextState, replace) {
if (auth.loggedIn()) {
replace({
pathname: '/',
state: { nextPathname: nextState.location.pathname }
});
}
}
export default (
<Route path='/' component={App}>
<IndexRoute component={CarsIndex} onEnter={requireAuth} />
<Route path='cars/new' component={CarNew} onEnter={requireAuth} />
<Route path='cars/:id' component={CarShow} onEnter={requireAuth} />
<Route path='cars/edit/:id' component={CarEdit} onEnter={requireAuth} />
<Route path='authenticate' component={SignIn} onEnter={filterLoggedIn} />
<Redirect from='*' to='/' />
</Route>
);
My server is a small express.js app and it redirects any requests other than /api/* to the index.html page.
Here's a part from my server.js:
const path = require('path');
const port = process.env.PORT || 1337;
const app = express();
const pathToStatic = path.join(__dirname, 'static');
express.static(path.join(__dirname, 'static'));
app.use(express.static(pathToStatic));
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.get('/api/cars', (req, res) => {
return Car.find((err, cars) => {
if (!err) {
return res.send(cars);
} else {
console.log(err);
res.statusCode = 500;
return res.send({ error: 'Server error' });
}
});
});
app.get('*', (req, res) => {
res.sendFile(path.resolve(pathToStatic, 'index.html'));
});
app.listen(port, () => {
console.log(`Express server is listening on port ${port}`);
});
Have you encountered such an issue? Could you help me to find out the soultion to this problem?
The screenshot shows that bundle.js (when requested) is returning index.html.
This is because your express routes handle route api/cars and then default everything else to index.html.
Of course all your resources that appear on index.html must also be sent the browser. This includes <script src="bundle.js"></script> which the browser will request once it gets index.html the first time.
So, you must have some way to allow express to handle requests for the resources that index.html needs.
A popular solution to this is to mount an assets directory and place that above the default route. Something like:
// api routes
app.use(express.static('assets'));
// default route
Then make sure your bundle.js is inside the assets directory. And then that the script tag looks like <script src="/assets/bundle.js"></script>.

Categories