I have single page app writen in Javascript using Express.js. Everything works well but i have little issue with images. I don't understand why is that happening but it looks like path of the images is incorrect but im 100% it's correct, cuz VSC shows me..
Tbh. This code below is not my work. I found Youtube tutorial how to make single page app in Javascript.
index.js
import dashboard from './views/dashboard.js';
import posts from './views/posts.js';
import settings from './views/settings.js';
....
const navigateTo = (url) => {
history.pushState(null, null, url);
router();
};
const router = async () => {
const routes = [
{ path: '/', view: dashboard },
{ path: '/posts', view: posts },
{ path: '/settings', view: settings },
....
];
//test each route for potential match
const potentialMatches = routes.map((route) => {
return {
route: route,
isMatch: location.pathname === route.path,
};
});
let match = potentialMatches.find(
(potentialMatches) => potentialMatches.isMatch
);
if (!match) {
match = {
route: routes[0],
isMatch: true,
};
}
const view = new match.route.view();
document.querySelector('#app').innerHTML = await view.getHtml();
// console.log(match.route.view());
};
window.addEventListener('popstate', router);
document.addEventListener('DOMContentLoaded', () => {
document.body.addEventListener('click', (e) => {
if (e.target.matches('[data-link]')) {
e.preventDefault();
navigateTo(e.target.href);
}
});
router();
});
server.js
const express = require('express');
const path = require('path');
const app = express();
app.use(
'/static',
express.static(path.resolve(__dirname, 'frontend', 'static'))
);
app.get('/*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'frontend', 'index.html'));
});
app.listen(process.env.PORT || 3000, () => console.log('Server running...'));
The menu where i have logo image which is not displayed is something like this. Nothing special under the logo i have clasic links. Do i must somehow import the image like all pages in index.js?
<div class="logo">
<a href="#">
<img
src="./logo.png"
alt="Logo" />
</a>
Related
I am currently having an issue with my React Application not working on any other browser other than Chrome. The Javascript loads just fine with no errors on Chrome and the application is currently fully deployed on Heroku (link: https://weathrd.herokuapp.com/).
In regards to my application, I have a search query set up in the "overview.js" component that creates a "get" request, with a parameter passed in that gets fed into the weather api I am using. Then, I retrieve the json information from the "/forecast" page and feed that back into "overview.js" to display on the screen.
I do not have any regex notation within any of my code, so I don't think that would be an issue here. I also have fully updated my Heroku deploy code and I do not think there is some sort of confusion on Heroku? Regardless, here is my server code, overview component code, and the error I am receiving on Safari:
server code:
const PORT = process.env.PORT || 8000;
const path = require('path');
const express = require('express');
const cors = require('cors');
const axios = require('axios');
require('dotenv').config();
const app = express();
app.use(cors());
app.use(express.static("public"))
app.get('/', (req, res) => {
res.json('hi');
});
app.get('/forecast', (req, res) => {
const options = {
method: 'GET',
url: `http://api.weatherapi.com/v1/forecast.json?`,
params: {
q: req.query.city,
key : process.env.REACT_APP_API_KEY,
days: '3',
api: 'no',
alerts: 'no',
},
};
axios.request(options).then((response) => {
res.json(response.data);
}).catch((error) => {
console.log(error);
});
});
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT} `))
Safari Error:
The error also mentions the component from which I am making the API request from "overview.js", so here is that code also:
overview.js
import React, {useState} from 'react';
import './overview.css';
import { RecentSearches } from '../Recent Searches/recentSearches';
import { Hourly } from '../Hourly/hourly';
import { Fiveday } from '../5 Day Forecast/fiveday';
import 'animate.css';
const axios = require('axios');
export function Overview() {
const [forecast, setForecast] = useState(null);
// this callback function receives the searched city entered from recentSearches and applies it to fetchForecast
const getSearch = (searchedCity) => {
fetchForecast(searchedCity);
};
async function fetchForecast(searchedCity) {
const options = {
method: 'GET',
url: 'https://weathrd.herokuapp.com/forecast',
params: {city: searchedCity}
};
axios.request(options).then((response) => {
console.log(response.data);
setForecast(response.data);
}).catch((error) => {
console.log(error);
})
};
return (
<div>
<div className='jumbotron' id='heading-title'>
<h1>Welcome to <strong>Weathered</strong>!</h1>
<h3>A Simple Weather Dashboard </h3>
</div>
<div className='container-fluid' id='homepage-skeleton'>
<div className='d-flex' id='center-page'>
<RecentSearches getSearch={getSearch}/>
<Hourly forecast={forecast}/>
</div>
</div>
<Fiveday forecast={forecast}/>
</div>
)
};
Thanks for any assistance!
the problem i'm having is basically my app works fine in local but in production anywhere that i've used server side rendering returns 500 internal server error. the other parts of my site which are called normally like in useEffect or componentDidMount work completely fine, like my dashboard or authorization process works without a problem, but anywhere that i have used ssr returns 500.
Below is some examples of how i have handled my ssr pages.
index page:
import React from 'react';
import HomePage from '../components/homePage/index'
import { Api, GuestHeaders } from '../components/config'
const Home = (props) => {
return <HomePage {...props} />
}
export async function getServerSideProps() {
const Response = await Api.get(`/v1/index`, { headers: GuestHeaders })
return {
props: {
Detail: Response.data,
}
}
}
export default Home
here is my Api component:
import axios from 'axios';
const GuestHeaders = {
'Authorization': "",
'content-type': 'application/json'
}
const Api = axios.create({
baseURL: 'baseUrl'
})
export { Api, GuestHeaders };
here is my server.js:
// server.js
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
}).listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
and my next.config.js:
module.exports = {
basePath: '',
trailingSlash: false,
}
I am trying to upload an image and show it on the client side using base 64. I am successful in sending to mongodb but not in showing the image once it is uploaded.
My theory: I need to have the setDefaultImage function outside the setGetBaseFile function to fix to be able to see image on client side.
when I attempt to move it I get a "Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. The only way arround this error that I have found is to move the function back in the parent function.
Below is my code for image.js
const mongoose = require("mongoose");
const Image = mongoose.model("gallery");
const express = require('express');
const ImageRouter = express.Router();
const DIR = './public/';
/* upload image in base64 format, thereby, directly storing it in mongodb datanase
along with images uploaded using firebase storage */
ImageRouter.route("/uploadbase")
.post((req, res, next) => {
const newImage = new Image({
imageName: req.body.imageName,
imageData: req.body.imageData
});
newImage.save()
.then((result) => {
res.status(200).json({
success: true,
document: result
});
})
.catch((err) => next(err));
});
module.exports = ImageRouter;
Below is my client side code:
import React, { useState } from "react";
import Container from "react-bootstrap/Container";
import Card from "react-bootstrap/Card";
// import Button from "react-bootstrap/Button";
// import "./postverse.css";
import Form from "react-bootstrap/Form";
import axios from "axios";
import FileBase from 'react-file-base64';
import DefaultImg from '../../assets/default-image.jpg';
const GlobalPost = () => {
const API_URL = "http://localhost:5000";
const [baseImage, UseBaseImage] = useState(DefaultImg);
const [DefaultImage, setDefaultImage] = useState("");
// function to upload image once it has been captured
setDefaultImage({
baseImage: DefaultImg
});
// function to capture base64 format of an image
function setGetBaseFile(files) {
// create a local readable base64 instance of an image
UseBaseImage({
baseImage: files.base64
});
let imageObj = {
imageName: "base-image-" + Date.now(),
imageData: files.base64.toString()
};
axios.post(`${API_URL}/image/uploadbase`, imageObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using base64 format");
UseBaseImage("base")
}
})
.catch((err) => {
alert("Error while uploading image using base64 format")
UseBaseImage("base")
});
}
return (
<div className="globalpost">
<Container className="mt-5 ml-auto mr-auto">
<h1 className="text-center">
Post to
<span className="text-success"> ShareVerse</span>
</h1>
<Form
className="shadow p-3 mb-5 bg-white rounded"
action="/search"
method="post"
encType="multipart/form-data"
>
<Form.Group controlId="formBasicVerse">
<Form.Label><h5>Upload Image</h5></Form.Label>
<FileBase type="file"
multiple={false}
onDone={setGetBaseFile}
/>
<Card.Img src={baseImage} alt="upload-image"></Card.Img>
</Form.Group>
</Form>
</Container>
</div>
);
};
export default GlobalPost;
Below is my mongoose data.schema
const mongoose = require("mongoose");
//create schema
const ImagesSchema = new mongoose.Schema({
name: {
type: String,
default: "none",
required: true
},
imageData: {
// type: mongoose.Types.ObjectId,
type: String,
required: true,
},
});
module.exports = mongoose.model("gallery", ImagesSchema);
Below is my app.js
// file includes all app level config, middleware, and supporting libraries
const express = require("express"); //import express
const app = express(); //initalise app with express
const cors = require("cors");
const bodyParser = require("body-parser");
const logger = require("morgan");
const routes = require("./routes/DataRoutes");
const ImageRouter = require('./routes/image');
app.use(bodyParser.json());
//body-parser handles HTTP POST requests.
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//enable Cross-Origin Resource Sharing.
app.use(cors());
app.use(express.json());
// get log details of our app if needed
app.use(logger("dev"));
//middleware to import routes
app.use('/image', ImageRouter);
app.use("/", routes);
app.use('/search', express.static('search'));
module.exports = app;
So using axios, I am attempting to make a request to my express server, here is my Axios request:
/* #flow */
import type {
Dispatch,
GetState,
ThunkAction,
Reducer,
} from '../../types';
export const USERS_INVALID = 'USERS_INVALID';
export const USERS_REQUESTING = 'USERS_REQUESTING';
export const USERS_FAILURE = 'USERS_FAILURE';
export const USERS_SUCCESS = 'USERS_SUCCESS';
export const API_URL = '/api/articleList';
// Export this for unit testing more easily
export const fetchUsers = (axios: any, URL: string = API_URL): ThunkAction =>
(dispatch: Dispatch) => {
dispatch({ type: USERS_REQUESTING });
return axios.get(URL)
.then((res) => {
dispatch({ type: USERS_SUCCESS, data: res.data });
})
.catch((err) => {
dispatch({ type: USERS_FAILURE, err });
});
};
// Preventing dobule fetching data
/* istanbul ignore next */
const shouldFetchUsers = (state: Reducer): boolean => {
// In development, we will allow action dispatching
// or your reducer hot reloading won't updated on the view
if (__DEV__) return true;
const userListFetch = state.userListFetch;
if (userListFetch.readyStatus === USERS_SUCCESS) return false; // Preventing double fetching data
return true;
};
/* istanbul ignore next */
export const fetchUsersIfNeeded = (): ThunkAction =>
(dispatch: Dispatch, getState: GetState, axios: any) => {
/* istanbul ignore next */
if (shouldFetchUsers(getState())) {
/* istanbul ignore next */
return dispatch(fetchUsers(axios));
}
/* istanbul ignore next */
return null;
};
and here is my code on the express server:
//GET ARTICLES
app.get('/api/articleList', (req, res) => {
console.log('hello');
});
It does not log "hello" and it displays no errors, I just think I'm missing something for the Axios to reach my server...
I had something similar working on another app but cannot seem to implement it here, I've searched online and cannot find a solution, any help or advice is appreciated - thank you in advance!
NOTE: the Axios request works fine when the "API_URL" variable is
set to a myjson url link, so I know my action works fine, I just feel
like I'm missing something for it to reach my server
EDIT: Please see my entire server.js:
/* #flow */
import path from 'path';
import morgan from 'morgan';
import express from 'express';
import compression from 'compression';
import helmet from 'helmet';
import hpp from 'hpp';
import favicon from 'serve-favicon';
import React from 'react';
import { renderToString, renderToStaticMarkup } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { matchRoutes } from 'react-router-config';
import { Provider } from 'react-redux';
import chalk from 'chalk';
import createHistory from 'history/createMemoryHistory';
import configureStore from './redux/store';
import Html from './utils/Html';
import App from './containers/App';
import routes from './routes';
import { port, host } from './config';
const app = express();
// Using helmet to secure Express with various HTTP headers
app.use(helmet());
// Prevent HTTP parameter pollution.
app.use(hpp());
// Compress all requests
app.use(compression());
// Use morgan for http request debug (only show error)
app.use(morgan('dev', { skip: (req, res) => res.statusCode < 400 }));
app.use(favicon(path.join(process.cwd(), './build/public/favicon.ico')));
app.use(express.static(path.join(process.cwd(), './build/public')));
// Run express as webpack dev server
if (__DEV__) {
const webpack = require('webpack');
const webpackConfig = require('../tools/webpack/webpack.client.babel');
const compiler = webpack(webpackConfig);
app.use(require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
hot: true,
noInfo: true,
stats: 'minimal',
}));
app.use(require('webpack-hot-middleware')(compiler));
}
// Register server-side rendering middleware
app.get('*', (req, res) => {
if (__DEV__) webpackIsomorphicTools.refresh();
const history = createHistory();
const store = configureStore(history);
const renderHtml = (store, htmlContent) => { // eslint-disable-line no-shadow
const html = renderToStaticMarkup(<Html store={store} htmlContent={htmlContent} />);
return `<!doctype html>${html}`;
};
// If __DISABLE_SSR__ = true, disable server side rendering
if (__DISABLE_SSR__) {
res.send(renderHtml(store));
return;
}
// Load data on server-side
const loadBranchData = () => {
const branch = matchRoutes(routes, req.url);
const promises = branch.map(({ route, match }) => {
// Dispatch the action(s) through the loadData method of "./routes.js"
if (route.loadData) return route.loadData(store.dispatch, match.params);
return Promise.resolve(null);
});
return Promise.all(promises);
};
// Send response after all the action(s) are dispathed
loadBranchData()
.then(() => {
// Setup React-Router server-side rendering
const routerContext = {};
const htmlContent = renderToString(
<Provider store={store}>
<StaticRouter location={req.url} context={routerContext}>
<App />
</StaticRouter>
</Provider>,
);
// Check if the render result contains a redirect, if so we need to set
// the specific status and redirect header and end the response
if (routerContext.url) {
res.status(301).setHeader('Location', routerContext.url);
res.end();
return;
}
// Checking is page is 404
const status = routerContext.status === '404' ? 404 : 200;
// Pass the route and initial state into html template
res.status(status).send(renderHtml(store, htmlContent));
})
.catch((err) => {
res.status(404).send('Not Found :(');
console.error(`==> 😠Rendering routes error: ${err}`);
});
});
//----------------------------------------------------
//GET ARTICLES
app.get('/api/articleList', (req, res) => {
console.log('yoyoyo');
var indexLimit = parseInt(req.query.indexLimit, 10);
var articleId = req.query.articleId
var articles = [];
db.collection('articles')
.find()
.sort("dateAdded", -1)
.limit(indexLimit)
.toArray()
.then(result => {
articles = articles.concat(result);
}).then(() => {
res.send(articles);
}).catch(e => {
console.error(e);
});
});
//------------------------------------
//connect to mongo db
var db
const MongoClient = require('mongodb').MongoClient
MongoClient.connect('mongodb://##CHANGED###:test#ds123930.mlab.com:###/###', (err, database) => {
if (err) return console.log(err);
db = database
console.log('db connected');
})
if (port) {
app.listen(port, host, (err) => {
if (err) console.error(`==> 😠OMG!!! ${err}`);
console.info(chalk.green(`==> 🌎 Listening at http://${host}:${port}`));
// Open Chrome
require('../tools/openBrowser').default(port);
});
} else {
console.error(chalk.red('==> 😠OMG!!! No PORT environment variable has been specified'));
}
You will need to move your /api routes above:
app.get('*', (req, res) => {
...
}
Your call to /api/articleList is hitting that catch all route handler of '*' and responding to the request with the rendered page. When communicating with your api for data, you don't want a page render, you want the response from the api :)
Middleware is executed in order of appearance from top to bottom.
I'm trying to build a HMR for koa2 development with chokidar.
Changing text in ./middlewares/render triggers chokidar file-watch event, and require.cache cleared immediately as expected, but when I reload page, the text rendered actually not changed.
./index.js
const path = require('path');
const http = require('http');
const Koa = require('koa');
const chokidar = require('chokidar');
const views = require('./middlewares/views.js');
const render = require('./middlewares/render.js');
const PORT = 3000;
const app = new Koa();
app.use(views);
app.use(render);
const server = http.createServer(app.callback());
server.listen(PORT, function() {
console.log('server now listening on port: %s', PORT);
});
const watcher = chokidar.watch([
path.resolve(__dirname, './middlewares'),
]);
watcher.on('ready', function() {
watcher.on('all', function(event, filename) {
console.log('clearing module cache...');
Object.keys(require.cache).forEach(function (id) {
if (/[\/\\]middlewares[\/\\]/.test(id)) {
console.log('delete cache: %s', id);
delete require.cache[id];
}
});
});
});
./middlewares/views.js
const views = require('koa-views');
module.exports = views('./templates', {
http: 'ejs',
extension: 'ejs',
});
./middlewares/render.js
module.exports = async (ctx, next) => {
// CHANGING THIS TEXT, TRIGGERS CHOKIDAR FILE-WATCH EVENT.
// REQUIRE.CACHE SEEMS CLEARED IMMEDIATELY.
// BUT WHEN I RELOAD PAGE, TEXT DOES NOT CHANGES!
const content = 'test delete require.cache.';
await ctx.render('main.ejs', { content: content });
};
./templates/index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<title>Node Repo</title>
</head>
<body>
<div><%- content %></div>
</body>
</html>
Okay... This repository helped.
Previously
const views = require('./middlewares/views.js');
const render = require('./middlewares/render.js');
app.use(views);
app.use(render);
Now
app.use(async (ctx, next) => {
const views = require('./middlewares/views.js');
await views(ctx, next);
});
app.use(async (ctx, next) => {
const render = require('./middlewares/render.js');
await render(ctx, next);
});