Initialise connections and batch service on app start - javascript

This query is specific to Next JS version 13 in Beta state.
I want to kick off a batch service to run every 24hours and also connect to mongo db
when the application starts.
In the previous versions, this could have been achieved by calling relevant functions within
the app.js file. If this was a node app, I could have called em inside the main index.js file.
How would we do this in NextJS 13? At present I am calling them inside the main pages.tsx file
which would lead to localhost:3000/. It does work.
But is this correct or is there a better correct way to do this?
// location /src/app/page.tsx
import { checkOrInitializeDbConnection } from '#/utils/database/connection';
import { kickOff24hourlyBatchService } from '#/utils/batch/kickOff24hourlyBatchService';
const Home = () => {
checkOrInitializeDbConnection();
kickOff24hourlyBatchService();
return (
<div>
Home
</div>
)
}
export default Home;

Related

Include JSON files into React build

I know this question maybe exist in stack overflow but I didn't get any good answers, and I hope in 2020 there is better solution.
In my react app I have a config JSON file, it contains information like the title, languages to the website etc..
and this file is located in 'src' directory
{
"headers":{
"title":"chat ",
"keys":"chat,asd ,
"description":" website"
},
"languages":{
"ru":"russian",
"ar":"arabic",
"en":"English"
},
"defaultLanguage":"ru",
"colors":{
"mainColor":"red",
"primary":"green",
"chatBackGround":"white"
}
}
I want to make my website easy to edit after publishing it, but after I build my app, I can't find that settings.json file there in build directory.
I find out that files in public directory actually get included to build folder, I tried to put my settings.JSON in public,
but react won't let me import anything outside of src directory
I found other solutions like this one but didn't work
https://github.com/facebook/create-react-app/issues/5378
Also I tried to create in index.html a global var like (window.JSON_DATA={}), and attach a JS object to it and import it to App.js, but still didn't work.
How can I make a settings JSON file, and have the ability to edit it after publishing the app?
Add your settings.json to the public folder. React will copy the file to the root of build. Then load it with fetch where you need it to be used. For example if you need to load setting.json to the App.js then do the next:
function App() {
const [state, setState] = useState({settings: null});
useEffect(()=>{
fetch('settings.json').then(response => {
response.json().then(settings => {
// instead of setting state you can use it any other way
setState({settings: settings});
})
})
})
}
If you use class-components then do the same in componentDidMount:
class CustomComponent extends React.Component {
constructor(props) {
super(props);
this.state = {settings: null};
}
componentDidMount() {
fetch('settings.json').then(response => {
response.json().then(settings => {
this.setState({settings: settings});
})
})
}
}
Then you can use it in render (or any other places of your component):
function App() {
...
return (
{this.state.settings && this.state.settings.value}
)
}
The easiest way would be to require() the file on the server during server side rendering of the html page and then inline the json in the html payload in a global var like you mentioned window.JSON_DATA={}. Then in your js code you can just reference that global var instead of trying to use import.
Of course this approach would require you to restart your server every time you make a change to the json file, so that it get's picked up. If that is not an option then you'll need to make an api call on the server instead of using require().
You may want to look at using npm react-scripts (https://www.npmjs.com/package/react-scripts) to produce your react application and build. This will package will create a template that you can put your existing code into and then give you a pre-configure build option that you can modify if you would like. The pre-configured build option will package your .json files as well. Check out their getting started section (https://create-react-app.dev/docs/getting-started/)
If you don't want to go that route, and are just looking for quick fix, then I would suggest change your json files to a JS file, export the JS object and import it in the files you need it since you seem to be able to do that.
//src/sampledata.js
module.exports = {
sample: 'data'
}
//src/example.jsx (this can also be .js)
const sampledata = require('./sampledata');
console.log(sampledata.sample); // 'data'
you can use 'Fetch Data from a JSON File'
according to link
https://www.pluralsight.com/guides/fetch-data-from-a-json-file-in-a-react-app
example

Making a classic javascript 3rd party app work within an existing react/node app

I have an app that does video chat already built in react/node.
I am trying to add a feature that opens up a shared whiteboard that the participants in the video chat can use.
I did some research and AwwApp is the one I like the best for this shared whiteboard functionality.
AwwApp does not have react specific code examples or modules. However, they do provide a straightforward widget that uses a .js file they host.
I am trying to implement this classic javascript widget into my existing react app and running into some issues - likely due to something simple that I'm just missing.
Here's the simple AwwApp quick start with the js file to include and how to call it the classic way: https://awwapp.com/apis/V2/docs/
Here is the code from my "AwwBoard.js" file that I am using to create a component that renders the whiteboard anywhere I want to import it and return in react:
import React, { Component } from 'react';
import Cookies from 'universal-cookie';
import Script from 'react-load-script'
// Get cookie for meeting room that this whiteboard instance will appear in. Will be used after I get whiteboard working.
const cookieMgr = new Cookies();
const room = cookieMgr.get('room');
// The main AwwApp external javascript file to include, I use react-load-script to load it below
const awwAppScript = 'https://awwapp.com/static/widget/js/aww3.min.js';
// Create a React component I can display using <AwwBoard /> anywhere I want a whieboard to appear
class AwwBoard extends Component {
handleScriptCreate() {
this.setState({ scriptLoaded: false });
};
handleScriptError() {
this.setState({ scriptError: true });
};
handleScriptLoad() {
this.setState({ scriptLoaded: true });
};
// Function I created to render the board. Per https://awwapp.com/apis/V2/docs/ - should embed the whiteboard widget in the div with id aww-wrapper below
handleBoardRender() {
var aww = new AwwBoard('#aww-wrapper', {
});
return aww;
};
render() {
return (
<div id="aww-wrapper">
<Script
url={awwAppScript}
onCreate={this.handleScriptCreate.bind(this)}
onError={this.handleScriptError.bind(this)}
onLoad={this.handleScriptLoad.bind(this)}
/>
{this.handleBoardRender()}
</div>
)
};
};
export default AwwBoard;
Any guidance would be greatly appreciated. I'm sure there is an easy way to do this, I'm just not seeing it after doing some of my own trial and error.
Thanks!

Nuxt Dynamic Routing on Firebase

My Nuxt.js App has this structure:
/pages/index.vue
/pages/_slug/index.vue
When user gets /{any_page}, it will use the path to build the page content:
/pages/_slug/index.vue
<template>
<div>
{{slug}}
</div>
</template>
<script>
import fetch from 'isomorphic-fetch';
export default {
async asyncData({ params }) {
return { slug: params.slug }
}
}
</script>
This works perfectly when running the Nuxt App directly via yarn dev.
When I try to run this using firebase functions:
$ firebase serve --only functions,hosting
The static routes work perfectly, but the dynamic routes always render the default / page, instead of executing the dynamic one. How do I fix this?
If using nuxt generate, then the explanation below should work. If using nuxt build, look at this repo, it is a template i wrote for nuxt + firebase as ssr.
Generate says it skips over dynamic routes, unless you explicitly specify them, hence 404 errors. In your Nuxt config file, you want to specify those routes. Here is an example of fetching from the database during generation. Note that in my example below, the doc id is the slug for the page.
nuxt.config.js
generate: {
async routes() {
const { db } = require('./services/fireInit');
const qs = await db.collection('recipes').get();
return qs.docs.map(x => `/recipes/${x.id}`);
}
},
The downside to this approach is as you add new items via a CMS, you will need to rebuild/deploy your site each time if you want to be able to use these new links without a 404. CMS without redeploy does work if you navigate to new content 'organically'...404 only occurs on a page refresh or on jumping directly to the page from a hyperlink.

How do I authenticate a Vue.js progressive web app with the Microsoft Graph

I have a Vue.js app. This app is a progressive web app, so it's intended to primarily run on the client-side. However, during the initial start-up, I need to authenticate the user in my Azure Active Directory, get data associated with their account, and store it for offline use.
I have a server-side API in place already for retrieving the data associated with a user account. I also know how to store it for offline use. However, my question is: how do I authenticate with the Microsoft Graph from my Vue.js app? Everything I see relies on using Node.js middleware, but unless I'm misunderstanding something, my progressive web app isn't a Node.js app. It's just straight up JavaScript, HTML, and CSS.
If the user closes the app, then revisits it in a couple of days, I believe I would need to use the refresh token to get a new access token. Still, once again, everything I see relies on Node.js middleware. I believe I need a solution that works purely in Vue.js / JavaScript. Am I mistaken?
Updates
1) Installed the Microsoft Graph Client via NPM (npm install #microsoft/microsoft-graph-client --save). This installed v1.7.0.
2) In my Vue.js app, I have:
import * as MicrosoftGraph from '#microsoft/microsoft-graph-client';
import * as Msal from 'msal';
let clientId = '<some guid>';
let scopes = ['user.read'];
let redirectUrl = 'http://localhost:1234/'; // This is registered in Azure AD.
let cb = (message, token, error, tokenType) => {
if (error) {
console.error(error);
} else {
console.log(token);
console.log(tokenType);
}
}
let reg = new Msal.UserAgentApplication(clientId, undefined, cb, { redirectUrl });
let authProvider = new MicrosoftGraph.MSALAuthenticationProvider(reg, scopes);
The last line generates an error that says: export 'MSALAuthenticationProvider' (imported as 'MicrosoftGraph') was not found in '#microsoft/microsoft-graph-client'
The last line generates an error that says: export 'MSALAuthenticationProvider' (imported as 'MicrosoftGraph') was not found in '#microsoft/microsoft-graph-client'
This error occurs because the main script (lib/src/index.js) of #microsoft/microsoft-graph-client does not export that symbol. You'll notice that logging MicrosoftGraph.MSALAuthenticationProvider yields undefined. Actually, the main script is intended to be run in Node middleware.
However, #microsoft/microsoft-graph-client provides browser scripts that do make MSALAuthenticationProvider available:
lib/graph-js-sdk-web.js
browserified bundle (not tree-shakable)
sets window.MicrosoftGraph, which contains MSALAuthenticationProvider
does not export any symbols itself
import '#microsoft/microsoft-graph-client/lib/graph-js-sdk-web'
let authProvider = new window.MicrosoftGraph.MSALAuthenticationProvider(/* ... */)
demo 1
lib/es/browser/index.js
ES Modules (tree-shakable)
exports MSALAuthenticationProvider
import { MSALAuthenticationProvider } from '#microsoft/microsoft-graph-client/lib/es/browser'
let authProvider = new MSALAuthenticationProvider(/* ... */)
demo 2
lib/src/browser/index.js
CommonJS module (not tree-shakable)
exports MSALAuthenticationProvider
import { MSALAuthenticationProvider } from '#microsoft/microsoft-graph-client/lib/src/browser'
let authProvider = new MSALAuthenticationProvider(/* ... */)
demo 3

How to use React to build a multi-page app?

I am building an app with NodeJS and would like to use React for some of the interactive components across the application. I do not want to make it single page app.
How do I break up or bundle my React components across a multi-page app?
Currently all my components are in one file even though I may never load them in some sections of the app.
So far I am trying using conditional statements to render components by searching for the ID of the container where React will render. I am not 100% sure of what the best practices are with React. It looks something like this.
if(document.getElementById('a-compenent-in-page-1')) {
React.render(
<AnimalBox url="/api/birds" />,
document.getElementById('a-compenent-in-page-1')
);
}
if(document.getElementById('a-compenent-in-page-2')) {
React.render(
<AnimalBox url="/api/cats" />,
document.getElementById('a-compenent-in-page-2')
);
}
if(document.getElementById('a-compenent-in-page-3')) {
React.render(
<AnimalSearchBox url="/api/search/:term" />,
document.getElementById('a-compenent-in-page-3')
);
}
I am still reading the documentation and haven't found what I need yet for a multi page app.
Currently, I am doing something similar.
The application is not a full React App, I am using React for dynamic Stuff, like CommentBox, which is autark. And can be included at any Point with special params..
However, all my sub Apps are loaded and included into a single file all.js, so it can be cached by the browser across pages.
When I need to include an App into the SSR Templates, I just have to include a DIV with the class "__react-root" and a special ID, ( the name of the React App to be rendered )
The logic is really simple:
import CommentBox from './apps/CommentBox';
import OtherApp from './apps/OtherApp';
const APPS = {
CommentBox,
OtherApp
};
function renderAppInElement(el) {
var App = APPS[el.id];
if (!App) return;
// get props from elements data attribute, like the post_id
const props = Object.assign({}, el.dataset);
ReactDOM.render(<App {...props} />, el);
}
document
.querySelectorAll('.__react-root')
.forEach(renderAppInElement)
<div>Some Article</div>
<div id="CommentBox" data-post_id="10" class="__react-root"></div>
<script src="/all.js"></script>
Edit
Since webpack perfectly supports code-splitting & LazyLoading, I thought it make sense to include an example where you don't need to load all your apps in one bundle, but split them up and load on demand.
import React from 'react';
import ReactDOM from 'react-dom';
const apps = {
'One': () => import('./One'),
'Two': () => import('./Two'),
}
const renderAppInElement = (el) => {
if (apps[el.id]) {
apps[el.id]().then((App) => {
ReactDOM.render(<App {...el.dataset} />, el);
});
}
}
You can provide several entry points for the application in the webpack.config.js file:
var config = {
entry: {
home: path.resolve(__dirname, './src/main'),
page1: path.resolve(__dirname, './src/page1'),
page2: path.resolve(__dirname, './src/page2'),
vendors: ['react']
},
output: {
path: path.join(__dirname, 'js'),
filename: '[name].bundle.js',
chunkFilename: '[id].chunk.js'
},
}
then you can have in your src folder three different html files with their respective js files (example for page1):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Page 1</title>
</head>
<body>
<div id="app"></div>
<script src="./vendors.js"></script>
<script src="./page1.bundle.js"></script>
</body>
</html>
JavaScript file:
import React from 'react'
import ReactDom from 'react-dom'
import App from './components/App'
import ComponentA from './components/ReactComponentA'
ReactDom.render(<div>
<App title='page1' />
<ReactComponentA/>
</div>, document.getElementById('app'))
Different React components can be then loaded for each single page.
I'm building an application from the ground up and am learning as I go, but I think what you are looking for is React-Router. React-Router maps your components to specific URLs. For example:
render((
<Router>
<Route path="/" component={App}>
<Route path="api/animals" component={Animals}>
<Route path="birds" component={Birds}/>
<Route path="cats" component={Cats}/>
</Route>
</Route>
<Route path="api/search:term" component={AnimalSearchBox}>
</Router>
), document.body)
In the search case, 'term' is accessible as a property in the AnimalSearchBox:
componentDidMount() {
// from the path `/api/search/:term`
const term = this.props.params.term
}
Try it out. This tutorial is the one that put me over the top in terms of my understanding of this and other related topics.
Original answer follows:
I found my way here looking for the same answer. See if this post inspires you. If your application is anything like mine, it will have areas that change very little and varies only in the main body. You could create a widget whose responsibility it is to render a different widget based upon the state of the application. Using a flux architecture, you could dispatch a navigation action that changes the state your body widget switches upon, effectively updating the body of the page only.
That's the approach I'm attempting now.
Are you using a CMS? They tend to like changing urls which could break your application.
Another way is using something like React Habitat.
With it, you can register components and they automatically get exposed to the dom.
Example
Register component(s):
container.register('AnimalBox', AnimalBox);
container.register('AnimalSearchBox', AnimalSearchBox);
Then they are availiable in your dom like this:
<div data-component="AnimalBox"></div>
<div data-component="AnimalSearchBox"></div>
The above will be automatically replaced with your react components.
You can then automatically pass properties (or props) to your components too:
<div data-component="AnimalBox" data-prop-size="small"></div>
This will expose size as a prop to your component. There are additional options for passing other types such as json, array's, ints, floats etc.
I know it's been a while since this question was asked but hopefully this helps someone.
As #Cocomico mentioned you could provide several entry points for the application in the webpack.config.js file. If you are looking for a simple Webpack setup (based on the idea of multiple entry points) that allows you to add React components to static pages you may consider using this: https://github.com/przemek-nowicki/multi-page-app-with-react
I revive this old question since I was in the same situation, without finding an answer that could satisfy my needs. So, based on #webdeb 's answer, I wrote a mini-framework that use CRA (without eject) to inject as many components you want in any HTML page while preserving all the CRA's benefits.
TL;DR
You can check my public repo here that contains all the needed files and a link to a Medium article where I thoroughly explain all this stuff.
The general idea
The trick is to install CRA as you normally would, and update the index.js file as follows :
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import reportWebVitals from './reportWebVitals';
//list here all the components that could be inserted in a web page
const apps = {
'App': React.lazy(() => import('./App')),
'TestComponent1': React.lazy(() => import('./TestComponent1')),
'TestComponent2': React.lazy(() => import('./TestComponent2')),
}
//event manager to communicate between the components
const bridgeEvent = new EventTarget();
//common fallback for all the components
function Fallback() {
return <div>Loading...</div>;
}
const renderAppInElement = (el) => {
if(apps[el.dataset.reactComponent] && !el.dataset.rendered){
//get the component's name stored in the data-react-component attribute
const App = apps[el.dataset.reactComponent];
//render the component, inject all the HTML attributes and the Event bridge
ReactDOM.render(
<Suspense fallback={<Fallback />}>
<App {...el.dataset} bridgeEvent={bridgeEvent}/>
</Suspense>
, el);
el.dataset.rendered = true;
}
else if(el.dataset.rendered){
console.log('el', el, 'is already rendered')
}
}
//ONLY FOR THE DEV PHASE
const rootEl = document.getElementById('root');
//generate components without attributes
if(process.env.REACT_APP_RENDER_CMP){
const components = process.env.REACT_APP_RENDER_CMP.split(',');
components.forEach(item => {
const componentEl = document.createElement('div');
componentEl.setAttribute("data-react-component", item);
componentEl.className = "__react-cmp";
rootEl.append(componentEl);
});
}
//generate components with attributes
if(process.env.REACT_APP_RENDER_CMP_WITH_ATTRS){
let componentsWithAttrs;
try{
componentsWithAttrs = JSON.parse(process.env.REACT_APP_RENDER_CMP_WITH_ATTRS);
}
catch(e){
console.log('fail to parse REACT_APP_RENDER_CMP_WITH_ATTRS', e);
}
if(componentsWithAttrs){
componentsWithAttrs.forEach(cmp => {
const componentEl = document.createElement('div');
componentEl.setAttribute("data-react-component", cmp.class);
componentEl.className = "__react-cmp";
Object.keys(cmp.data).forEach(attrKey => {
componentEl.setAttribute(attrKey, cmp.data[attrKey]);
});
rootEl.append(componentEl);
});
}
}
//the default name of the global object is ReactComponents, but it could be customized via the REACT_APP_NAMESPACE environment variable
const appNamespace = process.env.REACT_APP_NAMESPACE || "ReactComponents";
window[appNamespace] = {
ready: false,
parseComponents(container){
//parse the container or the whole document and inject all the components in the containers that have a "__react-cmp" class
(container || document)
.querySelectorAll('.__react-cmp')
.forEach(renderAppInElement);
}
}
window[appNamespace].parseComponents();
window[appNamespace].ready = true;
//if dynamic parsing must be done via the window.ReactComponents.parseComponents() method
//check the availability of window.ReactComponents object via window.ReactComponents.ready property
//or define a window.ReactComponentsAsyncInit() method to be notified of the availability
if(typeof window[`${appNamespace}AsyncInit`] === 'function'){
window[`${appNamespace}AsyncInit`]();
}
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
reportWebVitals();
Then you can add REACT_APP_RENDER_CMP and/or REACT_APP_RENDER_CMP_WITH_ATTRS environment variables to test your components while using the CRA's development server. Your .env.development.local file could look like:
#this will render the TestComponent1 and TestComponent2 without any attributes
REACT_APP_RENDER_CMP="TestComponent1,TestComponent2"
#this will render TestComponent1 with the data-test-attribute attribute set to "test attribute value"
REACT_APP_RENDER_CMP_WITH_ATTRS="[{"class":"TestComponent1","data":{"data-test-attribute":"test attribute value"}}]"
After building your files, you should have your index.html file with all the .js and .css files you need to include in each page of your multi-page app that should load your React components. Don't forget to add INLINE_RUNTIME_CHUNK=false in your .env file to avoid any inline javascript!
Then, add the components' containers in the HTML pages where you want them to show. For instance:
<div class="__react-cmp" data-react-component="TestComponent1"></div>
The parseComponents() declared in the CRA's index.js file should be executed, grabbing your div with the .__react-cmp class, then use it as a container for your TestComponent1 React component.
In the dedicated repo and article I explain how you could change your build path with the CRA's BUILD_PATH environment variable (so you can host your built files in your server or in a CDN) and I provide a loader that will parse the built index.html file and dynamically insert all the needed .js and .css files in your page (so you just have to include the loader, instead of all the files). Here is how the loader looks like, assuming its file name is cmp-loader.js and hosted next to your built index.html file:
(async () => {
const head = document.getElementsByTagName('head')[0];
const scriptSrcRegexp = new RegExp('<script.*?src="(.*?)"', 'gmi');
//get the exact script's src as defined in the src attribute
const scriptSrc = scriptSrcRegexp.exec(document.currentScript.outerHTML);
//all the resources should be relative to the path of this script
const resourcesPath = (scriptSrc && scriptSrc.length > 1) ? scriptSrc[1].replace('cmp-loader.js', '') : '';
//get the index content
const indexHTML = await (await fetch(resourcesPath+'index.html', {cache:'reload'})).text();
//assume that all the .js and .css files to load are in the "static" folder
const reactCSSRegexp = new RegExp(`<link href="${resourcesPath}static\/css\/(.*?)\.css" rel="stylesheet">`, 'gm');
const reactJSRegexp = new RegExp(`<script (.*?) src="${resourcesPath}static\/js\/(.*?)\.js"><\/script>`, 'gm');
//grab all the css tags
const ReactCSS = [].concat(indexHTML.match(reactCSSRegexp)).join('');
//grab all the js tags
const ReactJS = [].concat(indexHTML.match(reactJSRegexp)).join('');
//parse and execute the scripts
const scriptsDoc = new DOMParser().parseFromString(ReactJS, 'text/html');
Array.from(scriptsDoc.getElementsByTagName('script')).forEach(item => {
const script = document.createElement('script');
[...item.attributes].forEach(attr => {
script.setAttribute(attr.name, attr.value)
})
head.appendChild(script);
});
//inject the CSS
head.insertAdjacentHTML('beforeend', ReactCSS);
})().catch(e => {
console.log('fail to load react-cmp', e)
});
I suggest you take a look at InertiaJS: https://inertiajs.com/
With Inertia you build apps just like you've always done with your server-side web framework of choice. You use your framework's existing functionality for routing, controllers, middleware, authentication, authorization, data fetching, and more.
The only thing that's different is your view layer. Instead of using server-side rendering (eg. Blade or ERB templates), the views are JavaScript page components. This allows you to build your entire front-end using React, Vue or Svelte.

Categories

Resources