I18next and Webpack - reactjs

I'm starting with Webpack and find problem with react i18next hook-useTranslation.
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyPlugin = require("copy-webpack-plugin")
module.exports = {
output: {
path: path.join(__dirname, '/dist'),
filename: 'index.bundle.js'
},
devServer: {
port: 3000,
host: 'localhost',
watchContentBase: true,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /nodeModules/,
use: {
loader: "babel-loader"
}
},
]
},
plugins: [
new HtmlWebpackPlugin(
{
template: './src/index.html'
}
),
new CopyPlugin({
patterns: [
{
from: path.join(__dirname, '/public') , to: path.join(__dirname, '/dist') // copy public folder, which contains locales for i18next
}
]
})
],
}
//i18next.js
import i18next from 'i18next'
import { initReactI18next } from 'react-i18next'
import HttpApi from 'i18next-http-backend'
i18next
.use(initReactI18next)
.use(HttpApi)
.init(
{
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
},
lng: "en_GB",
supportedLngs: ["en_GB","cs_CZ"]
})
export default i18next
//example.js
import React from 'react'
import { useTranslation } from 'react-i18next'
const Greeting = () => {
const { t } = useTranslation()
return (
<div>
{/* {t("common:greeting")} */}
</div>
)
}
export default Greeting
In browser nothing loads and in console print these errors
[WDS] Disconnected!
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: ...
I want have locales in public folder, not in assets, acording docs, it's better to keep code separated from translations. I tried everything, but I can't find any solution. Someone had a similar problem?

You didnt define a namespace, so loadPath: '/locales/{{lng}}/{{ns}}.json' wont find {{ns}}.json because you said that you have to find {t("common:greeting")}, the namespace that you are searching for is common .So in order to fix this change the loadPath: '/locales/{{lng}}/common.json'

Related

MFE module federation cannot find module for remote child import

Im trying to build out a micro frontend for the first time so my main application can support a sub application. I have it all working when everything is just rendering App.tsx (remote) but as soon as I try and render a child within the remote application I get the Cannot find module error from webpack
Container craco.congig.js
/* eslint-disable #typescript-eslint/no-var-requires */
const CracoEsbuildPlugin = require('craco-esbuild');
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;
module.exports = {
webpack: {
plugins: {
add: [
new ModuleFederationPlugin({
name: "Dashboard",
remotes: {
DigitalCanopy: "DigitalCanopy#//localhost:3001/remoteEntry.js",
},
shared: {
react: {
singleton: true,
strictVersion: true,
requiredVersion: deps['react']
},
"react-dom": {
singleton: true,
strictVersion: true,
requiredVersion: deps['react-dom']
},
},
}),
],
},
...
My remote craco.config.js
/* eslint-disable #typescript-eslint/no-var-requires */
const CracoEsbuildPlugin = require('craco-esbuild');
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;
module.exports = {
devServer: {
port: 3001
},
webpack: {
plugins: {
add: [
new ModuleFederationPlugin({
name: "DigitalCanopy",
exposes: {
"./DigitalCanopy": "./src/App.tsx",
},
filename: "remoteEntry.js",
shared: {
react: { singleton: true },
"react-dom": { singleton: true },
},
}),
],
},
configure: (webpackConfig) => ({
...webpackConfig,
output: {
...webpackConfig.output,
publicPath: "auto",
},
}),
},
plugins: [
{
plugin: CracoEsbuildPlugin,
options: {
esbuildLoaderOptions: {
// Optional. Defaults to auto-detect loader.
loader: 'tsx', // Set the value to 'tsx' if you use typescript
target: 'es2018',
},
esbuildMinimizerOptions: {
target: 'es2018',
css: true, // if true, OptimizeCssAssetsWebpackPlugin will also be replaced by esbuild.
},
skipEsbuildJest: false, // Optional. Set to true if you want to use babel for jest tests,
esbuildJestOptions: {
loaders: {
'.ts': 'ts',
'.tsx': 'tsx',
},
},
},
},
],
};
My remote App.tsx
import { createTheme, ThemeProvider } from '#mui/material';
import React from 'react';
import './App.css';
import App1 from './App1';
const baseTheme = createTheme({
...MUI THEME STUFF
});
function App() {
return (
<ThemeProvider theme={baseTheme}>
<div className="App">Digital Canopy App</div>;
<App1 />
</ThemeProvider>
);
}
export default App;
This works and renders fine until I try and render <App1 />
Then I get this error Module not found: Error: Can't resolve './App1' in ...
Any ideas? I feel like this should just work. Importing children components from within the remote is pretty standard doesnt seem unique but I cannot find anything similar online which makes me think Im missing something obvious.
Thanks for the help
My issue was I was missing the .tsx extension on the import. I have no idea why that is required by the sub application. My build configs are identical for typescript and my main app does not require that. I'll dig in and try and figure out why

Module not found: Error: Can't resolve FILE in PATH - Webpack or Babel issue

Src folder structure:
| index.tsx
|
+---components
| Nav.tsx
|
\---pages
login.tsx
index.tsx:
import { BrowserWindow, app } from "electron";
import React from "react";
import ReactDOM from "react-dom";
import Login from "./pages/login";
const main = async () => {
ReactDOM.render(<Login />, document.getElementById("app"));
};
main();
login.tsx:
import React from "react";
interface loginProps {}
const Login: React.FC<loginProps> = ({}) => {
return (
<div>
<h1>HelloWorld</h1>
</div>
);
};
export default Login;
webpack:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
entry: "./src/index.tsx",
module: {
rules: [
{
test: /\.tsx?$/,
use: ["babel-loader", "ts-loader"],
include: [path.resolve(__dirname, "src")],
},
],
},
output: {
publicPath: "public",
filename: "bundle.js",
path: path.resolve(__dirname, "public"),
},
resolve: {
fallback: {
path: require.resolve("path-browserify"),
},
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
}),
],
target: "electron-main",
};
babel:
{
"presets": ["#babel/preset-env", "#babel/preset-react"]
}
I can't provide much more information on this since the compiler only outputs the error mentioned in title:
ERROR in ./src/index.tsx
Module not found: Error: Can't resolve './pages/login' in '..\src'. Something is probably not linked correctly. webpack 5.5.1 compiled with 1 error in 4971 ms
The problem was that I needed to add resolving extensions in the webpack config. Under resolve add:
extensions: [".ts", ".tsx", ".json", ".js"]
Also after that polyfill is needed refer to this post if you need help about it.
Webpack cant resolve TypeScript modules
Change the import of login, you have exported the login as default, but imported as normal export.
index.tsx:
import { BrowserWindow, app } from "electron";
import React from "react";
import ReactDOM from "react-dom";
import Login from "./pages/login"; // <== Changed this line
const main = async () => {
ReactDOM.render(<Login />, document.getElementById("app"));
};
main();
Login.tsx
import React from "react";
interface loginProps {}
const Login: React.FC<loginProps> = ({}) => { //<== Changed this line
return (
<div>
<h1>HelloWorld</h1>
</div>
);
};
export default Login;

Minified React error #130 when building react module

I'm getting
Minified React error #130
when i import my react module to another application maybe the module build is wierd or not correct.
I referenced a similar question.
Uncaught Error: Minified React error #130
but i think i have it set up correctly.
src/Fetch
import React, {Component} from 'react';
import MyFetch from './MyFetch';
const Fetch = (props) => (
<div>
<form onSubmit={props.onSubmit}>
<input type="text" value={props.url} placeholder="enter a url" onChange={props.onChange}/>
</form>
<MyFetch url={props.url} method={props.method}/>
</div>
)
export default Fetch;
src/index.js
import Fetch from './Fetch';
export default Fetch
dist/index.js
import React from 'react';
import { render} from 'react-dom';
import Fetch from '../src'
const App = () => (
<Fetch />
);
render(<App />, document.getElementById("root"));
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const htmlWebpackPlugin = new HtmlWebpackPlugin({
template: path.join(__dirname, "dist/index.html"),
filename: "./index.html"
});
module.exports = {
entry: path.join(__dirname, "dist/index.js"),
output: {
path: path.join(__dirname, './'),
filename: 'index.js',
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components|build)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/react'],
plugins:['#babel/plugin-proposal-class-properties']
}
}
}
]
},
plugins: [htmlWebpackPlugin],
resolve: {
extensions: [".js", ".jsx"]
},
devServer: {
port: 3001
}
};
fixed it
plugins: [htmlWebpackPlugin,new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development')
}
})]
React - Minified exception occurred

Webpack 4: Module parse failed: Unexpected token

During my builds, webpack is giving me this error:
ERROR in ./client/components/App/index.tsx 15:9
Module parse failed: Unexpected token (15:9)
You may need an appropriate loader to handle this file type.
|
|
> const App: SFC = () => (
| <div style={{ background: "red" }}>
| <h3>test</h3>
# ./client/index.tsx 11:4-14:6 12:24-51
# multi react-hot-loader/patch ./client/index.tsx webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true
Here is my webpack.config.ts:
import CleanWebpackPlugin from "clean-webpack-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";
import path from "path";
import { Configuration, HotModuleReplacementPlugin } from "webpack";
const rootDir = ["..", "..", "..", ".."];
const distDir = ["..", ".."];
// this file lives in one place as `.ts` and another as `.js` grab the
// file extension to determine the include path relative to its location
const include =
path.extname(module.id) === ".ts"
? path.resolve(__dirname, "client", "index.tsx")
: path.resolve(__dirname, ...rootDir, "client", "index.tsx");
const exclude = /node_modules/;
const tsconfig = path.resolve(
__dirname,
...rootDir,
"config",
"tsconfig.client.json"
);
// development plugins
const plugins = [
new HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "..", "..", "..", "index.html")
}),
new CleanWebpackPlugin([path.resolve(__dirname, ...distDir, "*.*")], {
allowExternal: true,
root: __dirname,
verbose: true
})
];
// script for webpack-hot-middleware
const hotMiddlewareScript: string =
"webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true";
const webpackDevConfig: Configuration = {
context: path.resolve(__dirname, ...rootDir),
devtool: "source-map",
entry: {
app: ["react-hot-loader/patch", include, hotMiddlewareScript]
},
mode: "development",
module: {
rules: [
{
exclude,
include,
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
exclude,
include,
loader: "ts-loader",
options: {
configFile: tsconfig,
transpileOnly: true
},
test: /\.tsx?$/
},
{
enforce: "pre",
exclude,
include,
loader: "source-map-loader",
test: /\.js$/
}
]
},
optimization: {
nodeEnv: "development"
},
output: {
filename: "[name].bundle.js",
path: path.join(__dirname, ...distDir),
publicPath: path.join(__dirname, ...distDir, "static/")
},
plugins,
resolve: {
extensions: [".js", ".ts", ".tsx", "*"]
},
target: "web"
};
export default webpackDevConfig;
My App.tsx:
import React, { SFC } from "react";
import ReactDOM from "react-dom";
const App: SFC = () => (
<div style={{ background: "red" }}>
<h3>test</h3>
</div>
);
My index.tsx:
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./components";
ReactDOM.render(<App />, document.getElementById("app"));
// enables Hot Module Replacement (HMR)
if ((module as any).hot) {
(module as any).hot.accept("./components/App", () => {
// for HMR to work, `App` must be re-required
const NextApp = require("./components/App").default;
ReactDOM.render(<NextApp />, document.getElementById("app"));
});
}
My tsconfig.json:
{
"compilerOptions": {
"allowJs": true,
"jsx": "react",
"module": "commonjs",
...
}
}
The error itself seems to give the solution: You may need an appropriate loader to handle this file type., however, it is my understanding that ts-loader should be able to handle react.
Here is an example webpack.config provided by the ts-loader team used in an app that uses typescript and react. The set up is fairly similar to my own, however, I do not use webpack-dev-server, rather, I use webpack-dev-middleware.
The issue was resolved by niba's comment on the original question. It seems that ts-loader when given a single module to include will not traverse the linked modules. Removing the include field or using the folder name fixed this error for me.

React Router can't configure URLs on localhost and remote. Receiving 404 not found error

I've encountered weird situation. I believe that the issue is related to React Router V4 configuration.
I'm using the react-router-modal and React Router v4. With react-router-modal component I render a link to a modal window which have it's own unique URL. Once a link to a modal is clicked - the modal is opened and the URL is added to the address bar. So I could even access the modal window from a new tab with this url:http://localhost:8080/modal_1URL what is very crucial for me.
In dev mode (npm start) everything works fine, also once the invalid URL is entered the page just reloads and invalid URL is remained in address bar. (Not best case)
I thought that everything will work as it is in a production build. But here is a problems. Once the final build is uploaded to the remote server or localhost I get these errors:
1. Once the invalid URL is entered - I receive 404 Not Found
2. Once I try to access a modal with a straight URL (not clicked in loaded page) http://localhost:8080/modal_1 - 404 Not Found
No .htaccess is uploaded
The index.js is very simple and the whole application is onepage with various components:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { ModalContainer } from 'react-router-modal';
import LandingPage from './components/villages/Landing Page.js';
import WOW from 'wowjs';
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
class App extends React.Component {
componentDidMount() {
new WOW.WOW().init();
}
render() {
return (
<div>
<LandingPage />
</div>
)
}
}
ReactDOM.render(
<I18nextProvider i18n={i18n}>
<BrowserRouter>
<div>
<App />
<ModalContainer />
</div>
</BrowserRouter>
</I18nextProvider>,
document.getElementById('app')
);
And the component witch renders the modals looks like:
import React from 'react';
import ReactDOM from 'react-dom';
import { ModalRoute, ModalLink } from 'react-router-modal';
import { Link } from 'react-router-dom';
import ImageLoader from 'react-load-image';
function Preloader(props) {
return <img src="img/spinner.gif" className="img-responsive center-block image-loader" />;
}
function ModalOne(props) {
const { t } = props;
return (
<div className='basic__modal-content'>
...
</div>
);
}
const ExtendedModalOne = translate()(ModalOne);
class Items extends React.Component {
render() {
const { t } = this.props;
return (
<div className="container">
<ul>
<div id="works">
<li>
<Link to="/modal_1"> <img src="img/" /> </Link>
<h3>Name</h3>
</li>
</div>
</ul>
<ModalLink component={ExtendedModalOne} path={`/modal_1`} />
<ModalLink component={ExtendedModalTwo} path={`/modal_2`} />
</div>
)
}
}
module.exports = translate()(Items);
and webpack.config:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const PreloadWebpackPlugin = require('preload-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const StyleExtHtmlWebpackPlugin = require('style-ext-html-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const autoprefixer = require('autoprefixer');
const staticSourcePath = path.join(__dirname, 'css');
const sourcePath = path.join(__dirname);
const buildPath = path.join(__dirname, 'dist');
module.exports = {
stats: {
warnings: false
},
devtool: 'cheap-module-source-map',
devServer: {
historyApiFallback: true,
contentBase: './'
},
entry: {
app: path.resolve(sourcePath, 'index.js')
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].[chunkhash].js',
publicPath: '/'
},
resolve: {
extensions: ['.webpack-loader.js', '.web-loader.js', '.loader.js', '.js', '.jsx'],
modules: [
sourcePath,
path.resolve(__dirname, 'node_modules')
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.[chunkhash].js',
minChunks (module) {
return module.context && module.context.indexOf('node_modules') >= 0;
}
}),
new webpack.LoaderOptionsPlugin({
options: {
postcss: [
autoprefixer({
browsers: [
'last 3 version',
'ie >= 10'
]
})
],
context: staticSourcePath
}
}),
new webpack.HashedModuleIdsPlugin(),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'index.html'),
path: buildPath,
excludeChunks: ['base'],
filename: 'index.html',
minify: {
collapseWhitespace: true,
collapseInlineTagWhitespace: true,
removeComments: true,
removeRedundantAttributes: true
}
}),
new PreloadWebpackPlugin({
rel: 'preload',
as: 'script',
include: 'all',
fileBlacklist: [/\.(css|map)$/, /base?.+/]
}),
new webpack.NoEmitOnErrorsPlugin(),
new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.js$|\.css$|\.html$|\.eot?.+$|\.ttf?.+$|\.woff?.+$|\.svg?.+$/,
threshold: 10240,
minRatio: 0.8
})
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
},
include: sourcePath
},
{
test: /\.(eot?.+|svg?.+|ttf?.+|otf?.+|woff?.+|woff2?.+)$/,
use: 'file-loader?name=assets/[name]-[hash].[ext]'
},
{
test: /\.(png|gif|jpg|svg)$/,
use: [
'url-loader?limit=20480&name=assets/[name]-[hash].[ext]'
],
include: staticSourcePath
}
]
}
};
Your HTTP server should always send index.html file for any route.
Example NodeJS Express configuration:
// ...
const app = express();
app.get('*', (req, res) => {
res.sendFile('path/to/your/index.html')
})
// ...
As I know, for the Apache server you can use:
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
It tells to Apache server to rewrite everything to the index.html page file and let the client handle routing.

Resources