How to conditionally import file in Reactjs - reactjs

I have have a large application that monitors a file called routes.js . I can not change the file name or mess with routes.js at all. I need to load another file based on a useState variable from another component when a condition is met. This following code will need to be put in Apps.js example:
if (!change) {
import routes from "routes";
} else {
import routes from "newroutes"
}
Is this possible?

You can just alias the imports.
import routes_1 from "routes";
import routes_2 from "newroutes"
Then, you can just declare a variable: routes and assign the appropriate value to it.
routes = !change ? routes_1 : routes_2;

It's possible to do using Webpack lazy loading (if you build your app using Webpack):
import(/* webpackChunkName: "routes" */ './routes').then(module => {
const routes = module.default;
});
But probably you will need to adjust your build config. Also this import is returning a promise, so you should write your code in the callback.
Update: but in your case it seems you don't need it. You could do something like this:
import file1 from 'routes'
import file2 from 'newroutes'
let routes = file1
if (change) {
routes = file2
}
So you will have your routes variable unchanged

Related

Debugging webpack code splitting with React.lazy

I am using React.lazy to try to split off some code from my main application, but it's not working the way I expect and I'm having trouble figuring out how to debug.
My code looks something like:
// index.js
import React from 'react';
import { LibraryUtils } from './library/utils';
const Component = React.lazy(() => import('./component'));
...
// component.js
import React from 'react';
import LibraryComponent from './library/component';
...
What I want:
vendors.chunk.js: react
main.chunk.js: index.js
main-1.chunk.js: component.js
library-0.chunk.js: library/utils
library-1.chunk.js: library/component
index.html: main.chunk.js, library-0.chunk.js, vendors.chunk.js
async chunks: main-1.chunk.js, library-1.chunk.js
What I get:
vendors.chunk.js: react
main.chunk.js: index.js + component.js
library.chunk.js: library/utils + library/component
index.html: main.chunk.js, library.chunk.js, vendors.chunk.js
async chunks: none
As a result, my initial page load needs to load all JS and therefore has worse performance.
How can I force webpack to split library into multiple chunks, so that I can leverage async chunks? Even better, how do I go about debugging something like this?
My config looks something like this:
splitChunks: {
chunks: 'all',
cacheGroups: {
library: {
test: /[\\/]library[\\/]/,
},
},
}
The problem was an accidental inclusion of babel-plugin-dynamic-import-node, which transforms dynamic imports (required for async loading) into regular require's for node environments, thereby preventing any async chunks from being generated. The solution was to remove that (or only include it when running unit tests).

Get error module not found in component file

I'm trying to write unit test. I just import my component file inside test.file. When trying run test. It gives error module not found for file import in file component, while in file component the import is working fine.
Component file - example.tsx
import getConfig from '#/component/utils/appConfig'
jest.config.js
module.exports = {moduleDirectories:["node_modules","src"]}
test.spec.js
import example from '../../example.js'
describe('example',() => {
it('render component file',()=> {
const container = render(<example/>);
})})
after 'npm run test'
then, get error
cannot find module '#/component/utils/appConfig' from example.tsx
The way we usually use import is based on relative path.
.and .. are similar to how we use to navigate in terminal like cd .. to go out of directory and mv ~/file . to move a file to current directory.
Your Statement Should be like this...
import getConfig from './component/utils/appConfig'

Custom routes with defineRoutes

Does someone have some extra info about defineRoutes function in remix.config?
I have a this route:
{
"id": "routes/__main/city/$city", "path": "/city/:city",
"file":"routes/__main/city/$city.tsx"
}
and in defineRoutes I made something like this:
routes: async (defineRoutes) => {
return defineRoutes((route) => {
route("/citta/:city", "routes/__main/city/$city.tsx");
});
},
I want that both /citta/test and /city/test will go on the same file located here routes/__main/city/$city.tsx.
But when I run the code only the /citta/test route is active the other one /city/test will throw error.
As I read from the docs here https://remix.run/docs/en/v1/api/conventions#routes, what I want to achive should be possible.
Have I misunderstood the use of defineRoutes?
This can be solved without the use of defineRoutes. Revert your remix.config changes and let Remix handle the routing for you by placing your routes within app/routes.
Move routes/__main/city/$city.tsx in your app directory and add an additional folder structure app/routes/__main/citta/$city.tsx. So you have two folders /city and /citta next to each other. They will share all the nested routing that you introduced with __main.
Export the following from your app/routes/__main/citta/$city.tsx file:
import CityComponent from '~/routes/__main/city/$city';
// re-export loader, action, and all other functionality
export * from '~/routes/__main/city/$city'
// re-use the default export from your other route
export default CityComponent;
This lets you reuse the code from your city/$city.tsx file in citta/$city.tsx.
Note: Make sure to name both files in citta/ and city/ $city.tsx to avoid naming discrepancies. Otherwise your re-exported loader and action won't work as the parameter name differs.
I recently tried to colocate all my code in modules and re-export the page components from app/routes like this:
import LoginPage, from "~/modules/auth/LoginPage";
export * from "~/modules/auth/LoginPage";
export default LoginPage;
but I ran into React 18 hydration issues. The working solution for me was re-exporting this way:
import LoginPage, { action, loader } from "~/modules/auth/LoginPage";
export { action, loader };
export default LoginPage;

React multiple imports of same file

I know the doubt is very basic but I haven't found an answer yet.
Here it goes:
Let's say I have a utils file that exports some function and I'm using that same utils function in many of my React components.
This is what I'm currently doing:
component1:
import { parseData } from '#/utils/parser';
component2:
import { parseData } from '#/utils/parser';
How can I avoid importing it on every file?
If you are using webpack there is a plugin called PluginProvider:
And maybe you can use it like this:
new webpack.ProvidePlugin({
parseData: path.resolve(path.join(__dirname, '#/utils/parser'),
});

How does React Lazy fetch components?

I recently read about React Lazy and how it "loads" the components during run time when they are required to be rendered. I assume that "loading" here means fetching the component from the server and then rendering it.
So my question is, how does React manage this fetching of components? How does it know the exact path from where to fetch this component (given that our code will mention the relative path but fetching will require complete server path)? Does it depend on Webpack for this?
Let's look into the React code. React.lazy is defined as follows.
export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
let lazyType = {
$$typeof: REACT_LAZY_TYPE,
_ctor: ctor,
// React uses these fields to store the result.
_status: -1,
_result: null,
};
if (__DEV__) {
// ... additional code only in development mode
}
return lazyType;
}
As you can see, React.lazy requires a Promise which resolves to a module with a default export containing a React component (freely cited by React Docs). This also means that not React resolves the file, but import() does. import() works as documented in the MDN.
The async import() is a new function in ES6 which is not available in all browsers but can be polyfilled by Webpack and Babel/Typescript/others.
What you often see is code like the following, which automatically splits the imported file away by Webpack.
import(/* webpackChunkName: "xyz" */ './component/XYZ')
This creates a new javascript xyz.js next to your bundle script.
If you don't use Webpack, you need to create those files by yourself. Webpack just reduces the work required from you. So you don't absolutely depend on Webpack. This approach might look like the following:
// ./component/xyz.js
export default function() { return <div>Component</div> }
// ./main.js
const OtherComponent = React.lazy(() => import('./component/xyz.js'));
export default function() { return <div>Component</div> }
And the file structure:
| public
|---| main.js
|---| component
|---| --- | main.js
As you see, no webpack required. It just makes your life easier.

Resources