Custom routes with defineRoutes - reactjs

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;

Related

How to conditionally import file in 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

How to Wrapped up fetch files in 1 files name modules, and export each fetch files, and import it in other components

i have a lot of fetch that i need to wrapped it up in 1 files name modules, and then i want to use it in other components, just called the modules and the fetch name. what should i do?
i already tried using module.exports but im confused and dont know how to do it.
module.exports = {
fetchLogin: require('./fetchLogin').default,
fetchDailyStatement : require('./fetchDailyStatement').default,
fetchOrderHistory : require('./fetchOrderHistory').default,
fetchAccountSummary : require('./fetchAccountSummary').default
};
fetchLogin.js
export default {
fetch('http://192.168.1.12:9090/orderhistory/10/25/'+ date).then(res=> res.json())
}
is that the right thing to do to export modules in react js ? but it still error

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.

Is there a way to import an MDX or MD markdown file in React and use it in a data array?

I want to make a list of blog posts and therefor I thought it would be easy to use MDX because it helps with styling each blog text. But I don't know if it's possible to import a MDX file and put it in blogs.text.
I tried to use the npm package mdx.macro with it's function importMDX, but I get an error which says that the imported file is outside the src/.
mdx.macro documentation: https://www.npmjs.com/package/mdx.macro
import React, { lazy } from 'react';
import { importMDX } from 'mdx.macro';
const blog1 = lazy(() => importMDX('./blog1.md'));
export const blogs = [
{
title: "Hello World",
subtitle: "subtitle",
text: blog1
}
];
export default blogs;
I import this file in my blog and loop through all the items. But the importMDX keeps giving me the following error:
Module not found: You attempted to import
node_modules\.cache\mdx.macro\Content.6cbf05377c.mdx.js
which falls outside of the project src/ directory.
Relative imports outside of src/ are not supported.
Maybe there's an easier option than this?
Thanks in advance!
Adding to #mfakhrusy's answer , I had to change my blogs.js file to
import { mdx } from 'mdx.macro';
import Blog1 from './Blog1.js';
export const blogs = [
{
title: "My experiences as an intern working without getting paid",
subtitle: "And the difficulties that come along with being undervalued by a company",
text: <Blog1 />
}
];
export default blogs;
And my Blog1.js file contains this
import React from 'react';
import { mdx } from 'mdx.macro';
export const Blog1 = mdx`
# Don't Panic
Since we decided a few weeks ago to adopt the leaf as legal tender, we have, of course, all become immensely rich.
`
export default Blog1;
So now I can write blogs in markdown format and loop through them to show them on my website!
According to The create-react-app imports restriction outside of src directory
It's a restriction from CRA developer. You can try to eject your CRA app and try it again. (see eject script on package json), and remove ModuleScopePlugin from webpack config. Be careful though, eject is a one-way trip, you cannot go back.
It happens because from what I've seen from the doc, the package tries to generate a cache file which being imported later by the app, and CRA would prohibit that by throwing that error you encountered.

React application with external plugins

I'm building a React application bundled using Parcel or Webpack.
The application should be able to embed external React components
developed by third-parties and hosted elsewhere as modern javascript modules:
// https://example.com/scripts/hello-plugin.js
import React from 'react';
export default class HelloPlugin extends React.Component {
render() {
return "Hello from external plugin!";
}
}
Host application loads these components using asynchronous import like this, for example:
// createAsyncComponent.tsx
import * as React from 'react';
import { asyncComponent } from 'react-async-component';
export default function createAsyncComponent(url: string) {
return asyncComponent({
resolve: () => import(url).then(component => component.default),
LoadingComponent: () => <div>Loading {url}....</div>,
ErrorComponent: ({ error }) => <div>Couldn't load {url}: {error.message}</div>,
})
}
But looks like bundlers don't allow importing arbitrary urls as external javascript modules.
Webpack emits build warnings: "the request of a dependency is an expression" and the import doesn't work. Parcel doesn't report any errors, but fails when import(url) occurs at runtime.
Webpack author recommends using scriptjs or little-loader for loading external scripts.
There is a working sample that loads an UMD component from arbitrary URL like this:
public componentDidMount() {
// expose dependencies as globals
window["React"] = React;
window["PropTypes"] = PropTypes;
// async load of remote UMD component
$script(this.props.url, () => {
const target = window[this.props.name];
if (target) {
this.setState({
Component: target,
error: null,
})
} else {
this.setState({
Component: null,
error: `Cannot load component at ${this.props.url}`,
})
}
});
}
Also, I saw a similar question answered a year ago where the suggested approach also involves passing variables via a window object.
But I'd like to avoid using globals given that most modern browsers support modules out of the box.
I'm wondering if it's possible. Perhaps, any way to instruct the bundler that my import(url) is not a request for the code-split chunk of a host application, but a request for loading an external Javascript module.
In the context of Webpack, you could do something like this:
import(/* webpackIgnore: true */'https://any.url/file.js')
.then((response) => {
response.main({ /* stuff from app plugins need... */ });
});
Then your plugin file would have something like...
const main = (args) => console.log('The plugin was started.');
export { main };
export default main;
Notice you can send stuff from your app's runtime to the plugin at the initialization (i.e. when invoking main at the plugin) of the plugins so you don't end up depending on global variables.
You get caching for free as Webpack remembers (caches) that the given URL has already loaded so subsequent calls to import that URL will resolve immediately.
Note: this seems to work in Chrome, Safari & firefox but not Edge. I never bothered testing in IE or other browsers.
I've tried doing this same sort of load with UMD format on the plugin side and that doesn't seem to work with the way Webpack loads stuff. In fact it's interesting that variables declared as globals, don't end up in the window object of your runtime. You'd have to explicitly do window.aGlobalValue = ... to get something on the global scope.
Obviously you could also use requirejs - or similar - in your app and then just have your plugins follow that API.
Listen to the Webpack author. You can't do (yet) what you're trying to do with Webpack.
You will have to follow his suggested route.

Resources