Tell webpack to import modules with file name pattern - reactjs

Can someone help me to find a nice solution for my react routing architecture?
I have defined a decorator which i use on my class components to define, which components are pages and behind which route to render them. This works very well, but i still have to import my page components to have my decorator being executed to fill a list of routes which i use to render my routes inside my App component.
Does anybody know a way to configure webpack to check my src dir for Components like **/*Page.tsx and import them automaticly?
For more visual:
import React from 'react';
import { ThemeProvider } from 'styled-components';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { useTheme } from '#core/styled';
import { useRoutes } from '#core/navigation';
// I want to get rude of this imports
import '#features/test-a/pages/HomePage';
import '#features/test-b/pages/TestPage';
// /
export const App: React.FC = () => {
const theme = useTheme();
const routes = useRoutes();
return (
<BrowserRouter>
<ThemeProvider theme={theme}>
<Routes>
{Object.keys(routes).map((path) => {
const { element, options = {} } = routes[path];
const Component = element as Function;
return <Route key={`route#${path}`} path={path} {...options} element={<Component />} />;
})}
</Routes>
</ThemeProvider>
</BrowserRouter>
);
};
My decorator:
export function Route(path: string, options?: RouteOptions) {
return (target: typeof React.Component) => {
setRoute(path, {
options,
element: target
});
};
}
Example page component:
import React from 'react';
import { Route } from '#core/navigation/Route';
#Route('/')
export class HomePage extends React.Component<any, any> {
render() {
return <h1>Hallo Welt</h1>;
}
}
FYI: I am using craco in this project so i have access to the webpack configuration.

Related

Could not find react-redux context value; please ensure the component is wrapped in a <Provider>

I'm trying to do my exportable package in which I create the redux store. This is the code:
import React from "react";
import { Provider } from "react-redux";
import { ErrorPage } from "../components/shared";
import { MyCoreApplication } from "./MyCoreApplication";
import { configureStore } from "../tools/configureStore";
import { createCoreHelp } from "./createCoreHelp";
export const MyCore = ({ withLogs, applicationSagas, applicationReducers, app, cookieInfo }) => {
const help = createCoreHelp(applicationSagas, applicationReducers, app);
if (help.error) return <ErrorPage errorMessage={help.error} tooltipMessage={help.tooltip} />;
else {
const store = configureStore(withLogs, applicationSagas, applicationReducers);
return (
<Provider store={store}>
<MyCoreApplication app={app} cookieInfo={cookieInfo} />
</Provider>
);
}
};
MyCoreApplication is the same:
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { IntlProvider } from "react-intl";
import { BrowserRouter as Router } from "react-router-dom";
import { CookieBar, ErrorPage, LoadingSpinner, Notification } from "../components/shared";
import { getConfig, getConfigStore, fetchConfigEnded, isMobileMode, setMobileMode } from "../modules/configuration";
import { getLocale, setLocale, getMessages, fetchMessages } from "../modules/language";
export const MyCoreApplication = ({ app, cookieInfo }) => {
const dispatch = useDispatch();
const config = useSelector(getConfigStore);
const configLoaded = useSelector(fetchConfigEnded);
const mobileMode = useSelector(isMobileMode);
const messages = useSelector(getMessages);
const locale = useSelector(getLocale);
const actions = {
getConfig: () => dispatch(getConfig()),
setLocale: (locale) => dispatch(setLocale(locale)),
fetchMessages: (locale) => dispatch(fetchMessages(locale)),
setMobileMode: (checkMobile) => dispatch(setMobileMode(checkMobile)),
};
if (config === null && !configLoaded) dispatch(getConfig());
else {
if (!locale && config) actions.setLocale(config.defaultLanguage);
}
return config ? (
<IntlProvider messages={messages} locale={locale} defaultLocale={config.defaultLanguage}>
<Notification />
<Router>{app}</Router>
{cookieInfo && cookieInfo.useCookie && cookieInfo.infoLink && <CookieBar infoLink={cookieInfo.infoLink} />}
</IntlProvider>
) : configLoaded ? (
<ErrorPage />
) : (
<LoadingSpinner />
);
};
The goal of my package is to build a library which create redux store receiving sagas and reducer from the custom application. In this mode a developer who use this library hasn't to create the store because module has already done.
In this project I use webpack and after run build and pack npm commands I've got a .tgz file that I use in a create-react-app project as a dependency. In this create-react-app project I use my package in the following mode:
import React from "react-dom";
import ReactDOM from "react-dom";
import { MyCore } from "my_core";
import { appSagas } from "./store/appSagas";
import { appReducers } from "./store/appReducers";
import { App } from "./container/App";
import "./styles/index.scss";
const wrapper = document.getElementById("container");
wrapper &&
ReactDOM.render(
<MyCore
withLogs={true}
applicationSagas={appSagas}
applicationReducers={appReducers}
app={<App />}
/>,
wrapper
);
And this is the App code:
import React from "react";
import { Layout } from "antd";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { MyHeader as Header } from "../components/MyHeader.jsx";
import { Components } from "my_core";
const { MyFooter: Footer } = Components;
const { Content } = Layout;
export const App = () => (
<Layout>
<Router>
<Header />
<Content>
<Switch>
<Route exact path="/">
hello
</Route>
</Switch>
<Footer />
</Content>
</Router>
</Layout>
);
Finally, inside MyHeader I use useSelector as the following code shows:
import React from "react";
import { useSelector } from "react-redux";
import { Utility } from "my_core";
const { configUtils } = Utility;
export const MyHeader = () => {
const mobileMode = useSelector(configUtils.isMobileMode);
return mobileMode ? <div>Mobile Header</div> : <div>Desktop Header</div>;
};
When I start the create-react-app project I encounter this error:
useSelector() Error: Could not find react-redux context value; please ensure the component is wrapped in a <Provider>
The same identical code, if written inside MyCore package project, works fine.
How can I solve this problem?
At this link github.com/iBobo5/myCore.git you can find myCore project. It's enough to run npm install and npm run export to obtain .tgz file. Once it has been created copy in a create-react-app "test" project and install it with react and react-dom dependencies. Other dependencies are installed by the library. Inside "test" project try to replace the import as shown above and inside a component use useSelector or useDispatch. In this way you could be able to reproduce my issue
The problem is probably that you have react-redux as a dependency rather than a peerDependency in your package.json. So you will potentially have two versions in the application in which the library is consumed, which causes the bug you described.

React + Redux + Storybook: How to use connected react router's useParams when writing storybook stories?

I have a react component that grabs an id from the route and uses that to load some data and populate the redux state.
I am using useParams from 'react-router' to do this.
import { useParams } from 'react-router'
import { usePreload } from './hooks'
import Display from './Display'
const Overview = () => {
const { id } = useParams()
const { data } = usePreload(id) // uses useEffect to preload the data with the given id
return <Display data={data} />
}
export default Overview
I've got a story
import Overview from './Overview'
import preloadData from './decorators/preloadData'
export default {
title: 'Redux/scenes/Overview',
decorators: [preloadData()],
component: Overview,
argTypes: {}
}
const Template = args => <Overview {...args} />
export const Default = Template.bind({})
The preloadData decorator is simply
import { usePreload } from '../hooks'
import { data } from './fixtures'
const Loaded = ({ children }) => {
useSubmissionsPreload(data.id) // loads the site data into the state
return <>{children}</>
}
const preloadData = () => Story => (
<Loaded>
<Story />
</Loaded>
)
export default preloadData
The code all works fine when actually running in the site but when running within a story there is no :id in the path for useParams to pick up.
For now I am just going to skip this story and just test the Display component, but the completist in me demands to know how to get this to work.
I also had the problem and the comment from De2ev pointed me in the right direction. It did however not work directly and I had to make slight changes. In the end it worked with the following code:
import React from "react";
import { Meta } from "#storybook/react";
import MyComponent from "./MyComponent";
import { MemoryRouter, Route} from "react-router-dom";
export default {
title: "My Title",
component: MyComponent,
decorators: [(Story) => (
<MemoryRouter initialEntries={["/path/58270ae9-c0ce-42e9-b0f6-f1e6fd924cf7"]}>
<Route path="/path/:myId">
<Story />
</Route>
</MemoryRouter>)],
} as Meta;
export const Default = () => <MyComponent />;
I've faced the same problem with Storybook 6.3+ and React Router 6.00-beta and had to wrap the <Route> with <Routes></Routes> for it to work.
import React from "react";
import { Meta } from "#storybook/react";
import MyComponent from "./MyComponent";
import { MemoryRouter, Routes, Route} from "react-router";
export default {
title: "My Title",
component: MyComponent,
decorators: [(Story) => (
<MemoryRouter initialEntries={["/path/58270ae9-c0ce-42e9-b0f6-f1e6fd924cf7"]}>
<Routes>
<Route path="/path/:myId" element={<Story />}/>
</Routes>
</MemoryRouter>)],
} as Meta;
export const Default = () => <MyComponent />;
We have faced similar challenge when trying to create storybook for one of the pages. We found solution published on Medium -> link. All credits and special thanks to the author.
Solution is using MemoryRouter available in react-router.
In our solution we used storybook Decorators which return the story wrapped by MemoryRouter and Router ->
return ( <MemoryRouter initialEntries={["/routeName/param"]} <Route component={(routerProps) => <Story {...routerProps} />} path="/routeName/:paramName"/> </MemoryRouter>)
I hope this helps everyone who experienced the same challenge.
Faced the same issue and completed as below
export default {
title: 'Common/Templates/Template Rendering',
component: CasePage
}
// 👇 We create a “template” of how args map to rendering
const Template: Story<any> = (args: any) => {
const { path } = args
return (
<MemoryRouter initialEntries={path}>
<Route
component={(routerProps: any) => <CasePage {...routerProps} />}
path="/dcp/:caseId"
/>
</MemoryRouter>
)
}
export const TemplateBoxesRendering = Template.bind({})
TemplateBoxesRendering.args = { path: ['/dcp/FX77777'] }
export const TemplateBoxes = Template.bind({})
TemplateBoxes.args = { path: ['/dcp/FX22222'] }

react-router-dom useParams() inside class component

I'm trying to load a details view based on a react-router-dom route that should grab the URL parameter (id) and use that to further populate the component.
My route looks like /task/:id and my component loads fine, until I try to grab the :id from the URL like so:
import React from "react";
import { useParams } from "react-router-dom";
class TaskDetail extends React.Component {
componentDidMount() {
let { id } = useParams();
this.fetchData(id);
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
export default TaskDetail;
This triggers the following error and I'm unsure where to correctly implement useParams().
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
The docs only show examples based on functional components, not class based.
Version <= 5:
You can use withRouter to accomplish this. Simply wrap your exported classed component inside of withRouter and then you can use this.props.match.params.id to get the parameters instead of using useParams(). You can also get any location, match, or history info by using withRouter. They are all passed in under this.props
Using your example it would look like this:
import React from "react";
import { withRouter } from "react-router";
class TaskDetail extends React.Component {
componentDidMount() {
const id = this.props.match.params.id;
this.fetchData(id);
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
export default withRouter(TaskDetail);
Simple as that!
import React, { Component } from "react";
import { useParams } from "react-router-dom";
function withParams(Component) {
return props => <Component {...props} params={useParams()} />;
}
class TaskDetail extends React.Component {
componentDidMount() {
let { id } = this.props.params;
this.fetchData(id);
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
export default withParams(TaskDetail);
Since hooks wont work with class based components you can wrap it in a function and pass the properties along:
class TaskDetail extends React.Component {
componentDidMount() {
const { id } = this.props.params;
// ...
}
}
export default (props) => (
<TaskDetail
{...props}
params={useParams()}
/>
);
But, like #michael-mayo said, I expect this is what withRouter is already performing.
Params get passed down through props on the match object.
props.match.params.yourParams
source: https://redux.js.org/advanced/usage-with-react-router
Here is an example from the docs destructing the props in the arguments.
const App = ({ match: { params } }) => {
return (
<div>
<AddTodo />
<VisibleTodoList filter={params.filter || 'SHOW_ALL'} />
<Footer />
</div>
)
}
You can not call a hook such as "useParams()" from a React.Component.
Easiest way if you want to use hooks and have an existing react.component is to create a function then call the React.Component from that function and pass the parameter.
import React from 'react';
import useParams from "react-router-dom";
import TaskDetail from './TaskDetail';
function GetId() {
const { id } = useParams();
console.log(id);
return (
<div>
<TaskDetail taskId={id} />
</div>
);
}
export default GetId;
Your switch route will still be something like
<Switch>
<Route path="/task/:id" component={GetId} />
</Switch>
then you will be able to get the id from from the props in your react component
this.props.taskId
In react-router-dom-v6 you can easily use useParams() in functional components but when it gets to the class component you have to create HOC (higher-order component) because hooks don't support class components:
import { useNavigate, useParams } from "react-router-dom";
export const withRouter = (WrappedComponent) => (props) => {
const params = useParams();
const navigate = useNavigate();
return <WrappedComponent {...props} params={params} navigate={navigate} />;
};
Then export your component from your HOC and give your component as a parameter. like below:
export default withRouter(YourComponentName);
After that you can easily access the url id with this.props.params.id and you can navigate to other components with this.props.navigate("/YourPath")
React Route v5
Query params can be read and processed as JSON using withRouter and queryString as follow:
import React from "react";
import { withRouter } from "react-router";
import queryString from 'query-string';
class MyComponent extends React.Component {
componentDidMount() {
const params = queryString.parse(this.props.location.search);
console.log('Do something with it', params);
}
render() {
return <div>Hi!</div>;
}
}
export default withRouter(MyComponent);
SmujMaiku is rigth!!! His answer works perfectly. This is how work today with react-router v6
enter code here
import React ,{Component} from 'react'
import { useParams } from "react-router-dom";
import PokeDescription from '../components/PokeDescription'
class PokeInfoConteiner extends Component{
render(){
let urlPokemon= "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/"
const {idPokemon} = this.props.params
console.log(idPokemon)
return(
<div>
<PokeDescription pokeImage={`${urlPokemon}${idPokemon}.png?raw=true`}/>
<p>{}</p>
</div>
)
}
}
export default (props) => (
<PokeInfoConteiner
{...props}
params={useParams()}
/>)
in React Router V6 :
import React, {Component} from 'react';
import {useParams} from 'react-router-dom';
/* This is a higher order component that
* inject a special prop to our component.
*/
function withRouter(Component) {
function ComponentWithRouter(props) {
let params = useParams()
return <Component {...props} params={params} />
}
return ComponentWithRouter
}
class TaskDetail extends React.Component {
state={
id : ""
}
componentDidMount() {
this.setState({
id : this.props.params.id
})
}
static getDerivedStateFromProps(nextProps) {
return {
id : nextProps.params.id
}
}
fetchData = id => {
// ...
};
render() {
return <div>Yo</div>;
}
}
const HOCTaskDetail = withRouter(TaskDetail);
export default HOCTaskDetail;
React Route v6
My friends, I tried to use in class but I failed to find any doc about it. So after many hours of searching and trying hard this is (in function). Now (i.e when I'm writing this post) there is only limited resource about v6. But there are many for <v6.
Here I'm using useState,useEffect,useParams,axios.
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';
const Post = () => {
let { post_id } = useParams();
const [posts, setPosts] = useState({ post: null, countSecrets: 0, ui: '' });
useEffect(() => {
if (posts.countSecrets === 0) {
const doAxe = (a) => {
axios.get('https://jsonplaceholder.typicode.com/posts/' + post_id)
.then((res) => {
setPosts(s => ({ ...s, value: res.data }));
doUI(res.data)
// console.log(res.data)
});
}
setPosts(s => ({ ...s, countSecrets: s.countSecrets + 1 }));
doAxe()
}
}, [posts, post_id]);
let doUI = (x) => {
// console.log('x' + x.title)
const finalPost = (x !== null) ? (
<div className="post">
<h4 className="center">{x.title}</h4>
<p>{x.body}</p>
</div>
) : (
<div className="center">Loading posts...</div>
);
setPosts(s => ({ ...s, ui: finalPost }));
}
return (
<div className="container">
{posts.ui}
</div>
);
}
export default Post;
NOTE:
I faced useEffect looping. I prevented it with a key.
HOPE: This may help someone!
Reference:
using useParams
state inside function
preventing loop from useEffect
In react-router-dom v6, there is no hook such as withRouter therefore my advice to you is to convert your class-based component to a functional component to use useParams hook in your component otherwise you can create a higher-order component to pass your class-based component.
as you know the useParams() is a hook for react-router-dom.
you can not use this inside the componentDidMount() or useEffect() because both of them are method that called during the Mounting phase of the React Life-cycle i.e after the component is rendered.
you have a solution:
create or define another function outside the componentDidMount() to define useParams then call it inside the componentDidMount.
know every thing will be ok.
This is my working example. :)
import React, { Component } from "react";
import { useParams } from "react-router-dom";
function withParams(Component) {
return (props) => <Component {...props} params={useParams()} />;
}
class ProductDetails extends Component {
handleSave = () => {
// Navigate to /products
};
render() {
return (
<div>
<h1>Product Details - {this.props.params.id}</h1>
<button onClick={this.handleSave}>Save</button>
</div>
);
}
}
export default withParams(ProductDetails);
Hooks only work on functional components,
you have to make that ocmponent a functional component
Fixed by creating a wrapping function
I needed to pass params to my SaxjaxApp.js from index.js using react-router-dom v6.
In v6 Switch has been changed to Routes
I got the useParams working with a class component by following Mohamed MAZEK's idea in post 20 using a wrapping function.
I needed to access the sessionId part of the url when it was available.
ie in localhost:3000/shared/123XYZId
I needed the 123XYZId part.
make note of this line : <Route path="/shared/:sessionId" element={<SaxjaxAppWrapper />} /> in the index.js below.
:sessionId denotes that useParams has a property called sessionId, that can be accessed by:
const {sessionId} = useParams() from a functional component.
In my index.js file I did this:
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import "./styles/style.scss";
import SaxjaxAppWrapper from "SaxjaxAppWrapper";
import SaxjaxApp from "./SaxjaxApp";
const container = document.getElementById("root");
const root = ReactDOM.createRoot(container);
//INFO: to learn about react-roue-dom v6 https://reactrouter.com/en/v6.3.0/upgrading/v5
root.render(
// <React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/shared/:sessionId" element={<SaxjaxAppWrapper />} />
<Route path="/" element={<SaxjaxApp />} />
</Routes>
</BrowserRouter>
// </React.StrictMode>
);
This line <Route path="/shared/:sessionId" element={<SaxjaxAppWrapper />} /> calls my wrapping function, whereas the default path / just calls the class component.
I had to create a separate file to hold the wrapping function I don't know why:
import React from "react";
import { useParams } from "react-router-dom";
import SaxjaxApp from "SaxjaxApp";
function SaxjaxAppWrapper() {
//I use the params here and store them to pass as props
let { sessionId } = useParams();
return (
//I pass the sessionId from the url params as a prop to my SaxjaxApp class component here
<SaxjaxApp sessionId={sessionId} />
);
}
export default SaxjaxAppWrapper;
My class component:
import React, { Component } from "react";
import "./styles/style.scss";
class SaxjaxApp extends Component {
state = {
octave: 4,
};
constructor(props) {
super(props);
//... initialise stuff
}
//... a lot of methods
render() {
//Access the param here
const { sessionId } = this.props;
<>
<div>
keybordId={sessionId ? sessionId : "no id was passed"}
</div>
</>
);
}
}
export default SaxjaxApp;

How to make Relay and React Routing work properly?

I am starting out with Relay and trying to make my routing work properly.
Unfortunately, I am not having much of luck.
Here is the error I am getting:
Uncaught TypeError: Component.getFragment is not a function
Here is the code I have for your reference:
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import Relay from 'react-relay';
import {Router, Route, IndexRoute, browserHistory} from 'react-router';
import {RelayRouter} from 'react-router-relay';
import App from './modules/app';
import Home from './modules/home';
const AppQueries = {
store: (Component) => Relay.QL `query {
store {
${Component.getFragment('store')}
}
}`
};
ReactDOM.render(
<RelayRouter history={browserHistory}>
<Route path='/' component={App} queries={AppQueries}>
<IndexRoute component={Home}/>
</Route>
</RelayRouter>,
document.getElementById('ch-root'));
app.jsx
import React, {Component} from 'react';
import Relay from 'react-relay';
import Header from './ui/header';
import Footer from './ui/footer';
class App extends Component {
render() {
return (
<div id="ch-container">
<Header/>
<section id="ch-body">
{this.props.children}
</section>
<Footer/>
</div>
);
}
}
App = Relay.createContainer(App, {
fragments: {
store: (Component) => Relay.QL `
fragment on Store {
${Component.getFragment('store')}
}
`
}
});
export default App;
home.jsx
import React, {Component} from 'react';
import Relay from 'react-relay';
import Loader from './ui/loader';
import AccountSelector from './account/account-selector';
const APP_HOST = window.CH_APP_HOST;
const CURR_HOST = `${window.location.protocol}//${window.location.host}`;
class Home extends Component {
state = {
activeAccount: null,
loading: true
}
render() {
const {activeAccount, loading} = this.state;
if (loading) {
return <Loader/>;
}
if (!activeAccount && !loading) {
return <AccountSelector/>;
}
return (
<h1>Hello!</h1>
);
}
}
Home = Relay.createContainer(Home, {
fragments: {
store: () => Relay.QL `
fragment on Store {
accounts {
unique_id,
subdomain
}
}
`
}
});
export default Home;
UPDATE
I made few changes suggested by freiksenet as below. But that raises the following two issues:
What would happen when I change the route and a component other than Home gets rendered by the App component?
I now get this error:
Warning: RelayContainer: Expected prop store to be supplied to
Home, but got undefined. Pass an explicit null if this is
intentional.
Here are the changes:
index.jsx
const AppQueries = {
store: () => Relay.QL `query {
store
}`
};
app.jsx
import Home from "./home";
...
App = Relay.createContainer(App, {
fragments: {
store: () => Relay.QL `
fragment on Store {
${Home.getFragment('store')}
}
`
}
});
Fragment definitions don't actually get Components as arguments, but a map of variables of the container, you only need to use them to have conditional fragments based on variable values.
Relay Route queries don't accept any arguments.
Here are the changes you need to make.
Route queries in Relay just need to specify the root query you are going to retrieve in this route, without the fragment.
index.jsx
const AppQueries = {
store: () => Relay.QL `query {
store
}`
};
Your App component actually doesn't use any relay data, so you can just export normal component instead of container.
export default class App extends Component {
If you'd need to pass some Relay data to it, you don't need to include child fragments, because fragments inclusion is only needed when you directly render other containers as direct children (not as this.props.children).
Then in Router you need to move the queries to Home.
ReactDOM.render(
<RelayRouter history={browserHistory}>
<Route path='/' component={App}>
<IndexRoute component={Home} queries={AppQueries} />
</Route>
</RelayRouter>,
document.getElementById('ch-root'));

React: dynamic import jsx

This question related to dynamically importing JSX files into React.
Basically we have one main component that dynamically renders other components based on a structure stored in a database. The dynamic components are stored in a subdirectory "./Components". We statically define the this as:
import CompA from './Components/CompA';
import CompB from './Components/CompB';
var components = {
'CompA': CompA,
'CompB': CompB
}
and we render them using:
var type = 'CompA'
var Component = components[type];
...
<Component />
While this works fine, it is a bit too static for us. We have 100+ components (CompA/CompBs) and statically define them does not work.
Is it possible to import all JSX files in "./Compontents" and populate the components-array?
And, what would be really (really) nice would be if we could send the "./Components" path as a prop to the main components. And the main component would use this path to import the .jsx files. Like this:
<MainComponent ComponentPath="./SystemComponents">
Would use "./SystemComponents" as path for .JSX files and:
<MainComponent ComponentPath="./UserComponents">
Would use "./UserComponents" as import path.
What about having a components/index.js with contents:
export CompA from "./comp_a";
export CompB from "./comp_b";
Then you do:
import * as Components from "./components"
then you would use as:
<Components.CompA />
<Components.CompB />
...
Hope this helps.
I doubt you can load anything when sending path through component props, loading of the file should then happen inside the React component lifecycle methods which is not something I would recommend.
As of React 16.6.0, we can lazy-load components and invoke them on-demand.
The Routing
// We pass the name of the component to load as a param
<Switch>
…
<Route path="/somewhere/:componentName" component={MyDynamicComponent} />
</Switch>
views/index.js
import { lazy } from 'react';
const SomeView = lazy(() => import('./SomeView'));
const SomeOtherView = lazy(() => import('./SomeOtherView'));
export { SomeView, SomeOtherView };
MyDynamicComponent.js
import React, { Suspense, Component } from 'react';
import { PropTypes } from 'prop-types';
import shortid from 'shortid'; // installed separately via NPM
import * as Views from './views';
class MyDynamicComponent extends Component {
render() {
const {
match: {
params: { componentName },
},
} = this.props;
const Empty = () => <div>This component does not exist.</div>;
const dynamicComponent = (() => {
const MyComponent = Views[`${componentName}View`]
? Views[`${componentName}View`]
: Empty;
return <MyComponent key={shortid.generate()} />;
})();
return (
<>
<Suspense fallback={<div>Loading…</div>}>{dynamicComponent}</Suspense>
</>
);
}
}
MyDynamicComponent.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
componentName: PropTypes.string.isRequired,
}),
}),
};
export default MyDynamicComponent;
Usage
{items.map(item => (
<NavLink to={`/somewhere/${item.componentName}`}>
{item.name}
</NavLink>
))}
To complement #gor181's answer, I can add that exports must be this way:
export { default as CompA } from "./comp_a";
export { default as CompB } from "./comp_b";
Hope this might be helpful.

Resources