Setting up webpack to export to set folder - reactjs

I'm working on an old project still running jquery for frontend, java spring for the backend, and maven for building. I've been asked by my boss to introduce react into the stack so we can toy around with converting some of the pages.
My goal is to not impact the existing implementation to heavily and instead output the result of webpack into a defined directory. This way I can just point the backend at that location for pathing.
So far I have an apps folder in my workspace that contains all my react stuff that works on its own. This was generated using 'npx create-react-app folderName'.
I've somewhat read up on how to set the export but am generally confused. As a lot of resources I've found assume a new setup or a replacement of an existing setup. While I'm looking to only replace a single page currently.

I don't think create-react-app is the right tool here, since you don't create a complete application with React but incrementally add React code. I would suggest using Webpack on its own. This makes your application cleaner and easier to maintain with your other code.
If you want to keep your React code separate from your existing code you can create a library based on webpack's Authoring Libraries Guide. You can than render your components with ReactDOM.render() (Docs). Note that you can call this function (almost) unlimited times on one page which allows you to partially replace your existing code.
Replacing a page then means to create a root DOM element and call the render function:
<!-- page.html -->
<html>
<head></head>
<body>
<!-- more html -->
<div id="page-root" />
<!-- more html -->
</body>
</html>
import React from 'react'
import ReactDOM from 'react-dom'
import Page from './routes/YourPageComponent'
ReactDOM.render(<Page />, document.getElementById('page-root'));
The previous code means that you render your code in the new code which is transpiled by your webpack loaders (e.g. Babel-Loader, Typescript-Loader). If you want to render the code in the existing code, look at the Doc's about Webpack Libraries to export render functions into a global context. The following scripts are an example out of my head.
// components/PageExampleComponent.jsx
import React from 'react';
export default function({pageTitle="example"}){
return <div>
<h1>{pageTitle}</h1>
</div>
}
// libary/index.js
import PageExampleComponent from './components/PageExampleComponent';
export const MyLibrary = {
PageExampleComponent
}
The previous code requires the following (partial) Webpack config to export MyLibrary:
module.exports = {
//...
output: {
library: 'MyLibrary',
// based on a fast look into the docs, I think the following are optional:
libraryTarget: 'window',
libraryExport: 'default'
}
};
To render a component of this library, you need React and ReactDOM as scripts in your website - and of course your own library script. You can than call ReactDOM.render() in plain JavaScript:
ReactDOM.render(
React.createElement(window.MyLibrary.PageExampleComponent),
document.getElementById('page-root')
Another idea would be to move everything into Webpack. This might be easier, as you don't have barriers of different Javascript-Versions and dialects (e.g. with and without JSX support). You can even separate your jQuery code and your React code by using two entry points:
module.exports = {
//...
entry: {
oldCode: './src/jqueryIndex.js',
replacement: './src/reactIndex.js'
},
output: {
filename: "[name].js"
}
};

Related

Can I use react in some view of my laravel website

I have a laravel website that is currently live and is using blade template for the views and I am thinking of using react JS in some views.
I want to know if it's possible to use react in some views while still having the blade template rendering some views.
OR do I have to rewrite the whole website in react for this to work.
Ok here's some example code that might help you get started with React:
Write a test react app e.g. test.jsx in your resources/js (or .tsx if you're using typescript). It might look something like:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from './App';
const root = document.getElementById('react-root');
if (root) {
ReactDOM.render(
<App />,
root
);
}
Here App is the React component root for the feature you are replacing.
You can add this in your webpack.mix.js file as well to transpile it independently or import it in your app.js (which is easier). However in the later case you are loading this code in all views and if there is another page with an element with id react-root there will be problems.
Your blade will now become:
#extends('layouts.index')
#section('content')
<div id="react-root"></div>
#endsection
Of course here it is oversimplified. You can just replace only the part that has the feature you are transitioning with <div id="react-root"></div> and have react handle that part from then on.
You can also do this multiple times in a single .blade.php file by making multiple calls to ReactDOM.render on different root elements. React will be fully responsible for everything under the root elements it renders to and the rest of your code will continue to run as before. However be aware that code that relied on element selectors via e.g. jQuery should not be used to manipulate nodes that React manages as that may cause problems.

REACT import css and js files

My application needs to have two pages, one landing page and one admin page. Both pages use different themes. I could not integrate css and js files of these two pages in a single page application.
const jquery = require('mdbootstrap/js/jquery-1.11.3.min.js');
window.jQuery = jquery;
window.$ = jquery;
require('mdbootstrap/css/bootstrap.min.css');
require('template/homePage/js/plugins/owl-carousel/owl.carousel.css');
require('template/homePage/js/plugins/owl-carousel/owl.theme.css');
require('template/homePage/js/plugins/owl-carousel/owl.transitions.css');
require('template/homePage/css/animate.css');
require('template/homePage/js/plugins/YouTube_PopUp-master/YouTubePopUp.css');
require('template/homePage/css/preloader.css');
require('template/homePage/css/style.css');
require('mdbootstrap/js/popper.min.js');
require('mdbootstrap/js/bootstrap.min');
require('template/homePage/js/plugins/vivid-icons');
require('template/homePage/js/plugins/owl-carousel/owl.carousel.js');
require('template/homePage/js/plugins/YouTube_PopUp-master/YouTubePopUp.jquery.js');
require('template/homePage/js/plugins/wow/wow.js');
require('template/homePage/js/plugins/jquery.easing.min.js');
require('template/homePage/js/main');
this sample import not good work. And I need outside link css and js.
I have two problem one of them is $(...).scrollspy is not a function
other WOW is not a function.
None of them work in sequence.
When you want to import resources into your React app, you use imports like this:
// Import with variable assignation
import logo from './logo.png';
// Import without variable assignation
import './css/index.css'
You can read more about this in the create-react-app documentation:
https://create-react-app.dev/docs/adding-images-fonts-and-files/
You can read more about ES7 imports here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
I see what you're trying to do is to add libraries and resources to your app component, like if were a common html file, you can't do that in React , you need to find an implementation.
It is not recommended to use jQuery with React, because you use jQuery to write code in a simple and fast way to create complex implementations, now those complex implementations can be made with just React and JS, that is what React is designed for.
Now I understand that you might want to use jQuery even do is not that recommendend, so here is a link where you can get jQuery to install it as a plugin for your React app
https://www.npmjs.com/package/jquery
You would be able to import it to your component like this:
import $ from "jquery";
To use Bootstrap in your React app you check out the documentation of an implementation of Bootstrap for React, react-bootstrap:
https://react-bootstrap.github.io/getting-started/introduction

How to import React from global variable?

I have a special scenario. One web application is built upon React. Another JavaScript utility is on React, but that utility is loaded by script tag. So, the application and the utility is built isolatedly. As a result, both the web application bundle file and utility bundle file have React built inside.
Now, we want to make them share one copy of React. It is preferred to export React as global variable in web application code(e.g. global.React) so that the utility can use it directly.
The code in utility is still like below. Hopefully, webpack can ignore it and import React from global.React.
import React from 'react';
import ReactDOM from 'react-dom';
The question is: how to config webpack to tell the utiltiy not to bundle React?
Tested with some non-React application:
in index.html import React from CDN, this will define global React.
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
somewhere in application doing as below, resolves to React instance:
declare var React;
console.log('React is ', React);
So if your first bundle, registers React globally, you may use it.
Just keep in mind, that it considered as bad practice to import from global like this. It beats whole concept of webpack's modularity. For instance, if your first module, upgrades to some backward incompatible version of React, your second module will break eventually as well.
For small projects, might be ok.

Build once and deploy build file in multiple environments with minimal changes React and Webpack

In webpack.config file declared a variable to read in application.
let BASEURL = "http://127.0.0.1:8090";
with this approach I am not able to update BASEURL after npm run build. Every time I want to generate a new build for each environment if BASEURL changes.
Is there any way to build once and deploy build file in multiple environments with minimal changes?
Tl;dr: use AJAX and have the config either in react context or in a global variable.
Detailed answer:
It is indeed as you state, after the application is built with npm run build, the environment variables become hardwired and cannot be changed.
The official statement of create-react-app is it does not support the build once deploy many principle. From https://create-react-app.dev/docs/adding-custom-environment-variables/ :
The environment variables are embedded during the build time. Since Create React App produces a static HTML/CSS/JS bundle, it can’t possibly read them at runtime.
However, there are ways of achieving the principle, just a bit more complicated. The idea is, you need to get the value of a variable at runtime from an external source, e.g. AJAX. In more details, possible solutions may be (but are not limited to) following:
1. server-side placeholder replacement
This is a solution proposed by create-react-app in https://create-react-app.dev/docs/title-and-meta-tags/#injecting-data-from-the-server-into-the-page, introducing a custom placeholder and replacing it with the data on the server, before it is rendered to clients.
<!doctype html>
<html lang="en">
<head>
<script>
window.SERVER_DATA = __SERVER_DATA__;
</script>
While this works, it introduces a major overhead, because it leaves the whole backend implementation up to you. Depending on your tech stack, this may be very easy or also very complicated to implement.
2. a dynamic <script> that assigns variable values
A solution proposed in https://www.cotyhamilton.com/build-once-deploy-anywhere-for-react-applications/ utilizes the dynamic nature of javascript. In the dynamically downloaded config.js file, a value is assigned to the variable. In the rest of the React code, the variable is read and used. You can change the config.js file any time, without the need of recompiling the react app.
// public/config.js
const apiUrl = 'localhost:1337';
const env = 'development';
<!-- public/index.html -->
<script src="%PUBLIC_URL%/config.js"></script>
<script>
window.config = { apiUrl, env };
</script>
The main downside is that this does not support TypeScript, and your IDE or linter may complain that apiUrl and env are not defined. Especially in bigger projects, this approach may be hard to maintain.
3. dynamic config with AJAX, with TypeScript support
Based on the 2nd solution, this article https://profinit.eu/en/blog/build-once-deploy-many-in-react-dynamic-configuration-properties/ describes in a great detail how to best achieve the build once deploy many principle with create-react-app and what are pros and cons.
It proposes downloading the dynamic config as a JSON with AJAX. The main caveat is to make sure that the dynamic config is downloaded BEFORE some code tires to use it. In the context of React lifecycle, there are two ways of how to achieve this.
3.1 global variable
Download the dynamic config JSON from globalConfigUrl, store it in a global variable, and only then render the React app. Example in TypeScript:
// index.tsx:
import axios from "axios";
import React, {ReactElement} from "react";
import App from "./App";
import {globalConfig, globalConfigUrl} from "./configuration/config";
axios.get(globalConfigUrl)
.then((response) => {
globalConfig.config = response.data; // THIS IS THE IMPORTANT LINE
return <App />;
})
.catch(e => {
return <p style={{color: "red", textAlign: "center"}}>Error while fetching global config</p>;
})
.then((reactElement: ReactElement) => {
ReactDOM.render(
reactElement,
document.getElementById("root")
);
});
Full working example: https://codesandbox.io/s/build-once-deploy-many-global-config-object-dvpzr
3.2. React context
Wrap your <App> component with a react context provider containing the configuration (with undefined or some default value). Fetch the configuration first time App is rendered and then save its value to the context. React will take care of the rest and will propagate the value change!
Full working example: https://codesandbox.io/s/build-once-deploy-many-react-context-7lk7g
The basic idea is this. Check the article above / working example for all details:
// App.tsx
import {useConfig} from "./configuration/useConfig";
// ... in the method:
const { setConfig } = useConfig(); // the `useConfig` is a custom hook, wrapping a React context. See the full working example for all details
useEffect(() => {
axios
.get(dynamicConfigUrl)
.then((response) => {
setConfig(response.data);
})
}, [setConfig]);

Webpack: How can I combine two completely separate bundles using dynamic bundling

I have spent a lot of time looking into this, but to no avail. I am aware of how code splitting and dynamic bundling works in Webpack using the import promise API.
Howevr, my use case is that I have two completely separate bundles, generated separately using different webpack builds. To give you perspective, I am building React components and there is a requirement to dynamically load a react component into the page that has been compiled in a different process. Is this possible in react? I do have control over both webpack builds, so I can exclude dependencies, etc.
Update: I just looked at Vue.js, and how it allows developers to register Vue.js components and then reference them later in the code. I could potentially load my Vue.js component scripts before my page script. I'm trying to see if I can do something similar in React.
Did I understand you correctly: you have essentially got
a library of custom React components (built by Webpack build #1)
a React app that needs to use some (all) of these components (built by Webpack build #2, totally separate from #1)
?
If yes, then read on.
The "Is this possible in react?" question should instead be "Is this possible in Webpack?", and the answer is "Yes". The following is tested with Webpack 2, but should also work with v.1.
Let's call your projects Lib (your React component library) and App (the library consumer).
In the Lib project:
Create an entry point file, say index.js, that exports all the custom React components like this:
import {Button} from './button';
import {DatePicker} from './DatePicker';
import {TextBox} from './textBox';
export const MyComponentLib = {
Button,
DatePicker,
TextBox
};
Update webpack.config.js to make the project's bundle a UMD library (could also be 'var'), and set the entry point to the above index.js file. Doing so will make your library available via a global variable named MyComponentLib (the name comes from the export above) in the consuming app later on:
...
output: {
path: './dist',
filename: 'mylib.bundle.js',
libraryTarget: 'umd'
},
...
entry: './index.js',
...
On to the App project:
In the index.html file you will have two <script> tags: one for mylib.bundle.js (the output of the Lib project), and another for the bundle of the App project itself. You might have more bundles (app, vendor etc.), I'm just simplifying things here.
Update webpack.config.js to mark the component library as external dependency. Here, MyComponentLib is, again, the name of the global variable the library is available at, and myComponents is the name to use in import statements:
...
externals: {
myComponents: 'MyComponentLib'
},
...
Now, in App you can import a component like this:
import {DatePicker} from 'myComponents';
This will dynamically load DatePicker from the component library at run time via the global variable.
Bonus: if you use eslint, you don't want it to complain about missing modules that you know are external; add this to your .eslintrc:
...
"settings": {
"import/core-modules": ["myComponents"]
},
...

Resources