How to create a function that renders a component with map? - reactjs

I'm trying to render a component that is actually created from a request from the backend, which brings me an array and so on.
However, I am getting the following error in the component:
const RouteMicrofrontend: () => false | (JSX.Element | undefined)[][]
'RouteMicrofrontend' cannot be used as a JSX component.
Its return type 'false | (Element | undefined)[][]' is not a valid JSX element.
Type 'boolean' is not assignable to type 'ReactElement<any, any>'.ts(2786)
Is it possible to create a function that renders a component from .map? How to do this?
const RouteMicrofrontend = useCallback(
() =>
microfrontendNav.length > 0 &&
microfrontendNav.map(({ ListMenu }) =>
ListMenu.map(
({ microfrontend }) =>
microfrontend && (
<Route key={microfrontend.registry} path={microfrontend.data.basename}>
<MicrofrontendContainer>
<Loader
service={microfrontend.service}
module={microfrontend.module}
registry={microfrontend.registry}
data={microfrontend.data}
fallback={<MicrofrontendFallback />}
suspense={<MicrofrontendSuspense />}
/>
</MicrofrontendContainer>
</Route>
)
)
),
[microfrontendNav]
);
return (
<BrowserRouter>
<MicrofrontendNav sectionList={microfrontendNav} />
<TickerCarousel />
<Header />
<Switch>
<Route exact path='/' component={() => <div />} />
<Route exact path='/404' component={() => <Error404 />} />
<RouteMicrofrontend />
<Route path='*' component={() => <Redirect to='/404' />} />
</Switch>
</BrowserRouter>
);

The provided compiler error pretty much explains the whole problem. Type false | (Element | undefined)[][] is not a valid React component.
You cannot return multiple elements from a component function (DOM diffing, etc.). Change your code that the RouteMicrofrontend function returns: null when the conditions are not met or a single element with map contents as children.

Aside - You cannot return false from a React component. If you want to render nothing from a React component, return null.
You can conditionally render something (such as an array) using the ternary operator.
Imagine you want to render an Array<Item>:
interface ItemModel {
}
function Item({ item } : { item: ItemModel }) {
return (<div>Render Something Here</div>);
}
function MicroFrontend({ items }: { items: Item[] }) {
return (
<>
...
{ items.length && <h1>Items</h1> }
{ items.length > 0 ? items.map(item => <Item item={item} />) : null }
...
</>
);
}

Related

React hooks : 'Cannot read property 'push' of undefined'

I'm trying to redirect my homepage to "/call" page based on a redux state. I can go to that component by typing the url manually but cant do it with a function. I tried "Redirect to", "history.push" but none of them worked for me. I cant solve the problem. Here is my code;
const Phone = ({ hidden, photoOpened, inCall }) => {
const dispatch = useDispatch(getContacts());
let history = useHistory();
useEffect(() => {
if (inCall.inCall) {
history.push('/call')
}
}, [inCall]);
useEffect(() => {
dispatch(getContacts());
}, [])
return (
<div hidden={process.env.NODE_ENV === 'development' ? !hidden : hidden} className={photoOpened ? "phone-container-rotate" : "phone-container"}>
<div className="coque" />
<Suspense fallback={<div className="animated fadeIn pt-1 text-center">Loading...</div>}>
<HashRouter basename="/phone">
<div
className="phone-content"
style={{ backgroundImage: `url(${background})` }}
>
<HeaderBar />
<BannerNotifications />
<Switch>
{routes.map((route, idx) => {
return route.component ? (
<Route
key={idx}
path={route.path}
exact={route.exact}
render={props => <route.component {...props} />}
/>
) : null;
})}
</Switch>
</div>
<Route component={BottomPhoneNavigator} />
</HashRouter>
</Suspense>
</div>
);
};
You could try and test for history existence of the history in your effect, also add it to dependency list
useEffect(() => {
if (history && inCall.inCall) {
history.push('/call')
}
}, [inCall, history]);
And important thing, your component using this hook must be within the Router, I see you'\re using HashRouter but as child of component using the hook.
Also if you're stuck to this structure, why wont you try to use Redirect within the Switch? This could work with some smart test so you wont end up in a loop:)
To use history your Phone component should be inside router component

Combining 2 or more dynamic imports into one chunk for webpack

I am code splitting a react-webpack application based on which route is loaded in the browserRouter. Now I want to package 2 or more dynamic imports into one chunk. For example I have a route /A which renders the A Dashboard and another /A/{id} which shows the details view for a single item. Since, when my users navigate to the dashboard, there is a high possibility that they would also open the details view for some item, I want to package both the dynamically imported components in the same chunk. Is this possible?
let aDetails: any;
let aDashboard: any;
const [isLoadedADashboard, setIsLoadedADashboard] = useState<boolean>(false);
const [isLoadedADetails, setIsLoadedADetails] = useState<boolean>(false);
return (
<BrowserRouter>
<Route
exact
path="/A"
render={(props) => {
import('../A/Dashboard').then((mod) => {
aDashboard = mod.default;
setLoadedADashboard(true);
});
return loadedADashboard ? (
<aDashboard />
) : <div />;
}}
/>
<Route
exact
path="/A/{id}"
render={(props) => {
import('../A/Details').then((mod) => {
aDetails = mod.default;
setLoadedADetails(true);
});
return loadedADetails ? (
<aDetails />
) : <div />;
}}
/>
</BrowserRouter>
);
You can try using webpack magic comments inside the dynamic imports. For both the dynamic imports use the same webpackChunkName to ensure that both the files are bundled under same chunk.
import(/* webpackChunkName: 'dashboard' */'../A/Dashboard').then((mod) => {
aDashboard = mod.default;
setLoadedADashboard(true);
});
import(/* webpackChunkName: 'dashboard' */'../A/Details').then((mod) => {
aDetails = mod.default;
setLoadedADetails(true);
});
Note: we are using the same chunk name for both the imports.
Create proxy-module which includes both modules you need to be in one chunk:
import Dashboard from 'Dashboard';
import Details from 'Details';
export {Dashboard, Details};
Now let say the name of the module above is 'Proxy' (just for example) and it's located in the same directory as your modules. You can load it dynamically:
<Route
exact
path="/A"
render={(props) => {
import('../A/Proxy').then((mod) => {
aDashboard = mod.Dashboard;
setLoadedADashboard(true);
});
return loadedADashboard ? (
<aDashboard />
) : <div />;
}}
/>
<Route
exact
path="/A/{id}"
render={(props) => {
import('../A/Proxy').then((mod) => {
aDetails = mod.Details;
setLoadedADetails(true);
});
return loadedADetails ? (
<aDetails />
) : <div />;
}}
/>

I cannot render the data from my db in React.js project "TypeError: Cannot read property 'title' of undefined"

I'm using Axios to get data from my API but when I try to render the state out I get an error of : "TypeError: Cannot read property 'title' of undefined".
I used the same code for a previous component and it seemed to work.
Data looks like this:
[
{
"portfolio": {
"title": "Portfolio",
"subTitle": "Checkout my works"
},
"cv": {
"title": "Download my cv",
"subTitle": "Aviable in three languages"
},
"contact": {
"title": "Let's talk",
"subTitle": "Send me and e-mail"
},
"_id": "5eff2aefa386601f00c98b7d",
"__v": 0
}
]
code in App.js file :
I just used the state to pass data trough the portfolio component.
<Route path="/portfolio" render={() => (<Portfolio title={state.header.portfolio.title} subTitle={state.header.portfolio} />)} />
Entire code:
const App = () => {
const [state,setState] = useState({
header:[]
})
useEffect(() => {
axios.get('http://localhost:8080/api/metainfo')
.then(res=>res)
.then(data=>{
// console.log(data.data)
setState(prevState=>{
return{...prevState,header:data.data}
})
})
},[]);
return (
<Router>
<div className="App">
<NavBar />
<div className="app-wrap">
<Switch>
<Route path="/" exact render={Home} />
<Route path="/portfolio" render={() => (<Portfolio title={state.header.portfolio.title} subTitle={state.header.portfolio} />)} />
<Route path="/cv" component={CV} />
<Route path="/contact" component={Contact} />
<Route path="/footer" component={Footer} />
</Switch>
</div>
</div>
</Router>
);
}
Errors: Image with errors
Please help
First, instead of creating your own setState, I would change your state hook to be:
const [header, setHeader] = useState([]);
That is, assuming your data is an array. I'm not sure why it's an array, but that's what you've provided.
Next, your useEffect has an unnecessary .then() line in it. I would change it like so:
useEffect(() => {
axios.get('http://localhost:8080/api/metainfo')
.then(res=>{
// console.log(res.data)
setHeader(res.data)
// Use [...res.data] if necessary
})
},[]);
Finally, because your header state object is an array, you will need to reference it in your Route like so:
<Route path="/portfolio" render={() => (<Portfolio title={header.length > 0 ? header[0].portfolio.title : ''} subTitle={header.length > 0 ? header[0].portfolio.subTitle : ''} />)} />
I've edited my answer to include that from blankart.
It seems like your error comes from the initial load of your Component. In order to fix this, change your Portfolio props
<Portfolio title={state.header.length > 0 ? state.header[0].portfolio.title : ''} subTitle={state.header.length > 0 ? state.header[0].portfolio.subTitle : ''} />

React collect all properties as one props and put it into component or NOT?

I'm reading this following code from https://reacttraining.com/react-router/web/example/route-config
const RouteWithSubRoutes = route => (
<Route
path={route.path}
render={props => (
// pass the sub-routes down to keep nesting
<route.component {...props} routes={route.routes} />
)}
/>
);
const RouteConfigExample = () => (
<Router>
<div>
<ul>
<li>
<Link to="/tacos">Tacos</Link>
</li>
<li>
<Link to="/sandwiches">Sandwiches</Link>
</li>
</ul>
{routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
</div>
</Router>
);
Please look at the first line, why it's not this:
const RouteWithSubRoutes = ({route}) => (
As I know, this arrow function should get one parameter which we often call it as props, which should be a collection include all properties that be put in. In this case the props should include 'key' and all of properties of 'route'.
In the component of the arrow function, the RouteWithSubRouters, we should filter the useful properties from the collection props, such as route, so we write the parameters as ({route}).
Am I get it wrong? Why it show error when I change it to ({route})?
===================================================================
Thanks to all! Now I know the parameter magic. I change code as following:
const RouteWithSubRoutes = (routeProps) => {
console.log(routeProps.aaa)
return (
<Route
path={routeProps.path}
render={props => (
// pass the sub-routes down to keep nesting
<routeProps.component {...props} routes={routeProps.routes} />
)}
/>
);
const RouteConfigExample = () => (
<Router>
<div>
<ul>
<li>
<Link to="/tacos">Tacos</Link>
</li>
<li>
<Link to="/sandwiches">Sandwiches</Link>
</li>
</ul>
{routes.map((route, i) => <RouteWithSubRoutes key={i} aaa="bbb" {...route} />)}
</div>
</Router>
);
I get the print 'bbb'~.
It's easier to understand if the parameter is named as 'props'.
In this code route is an object that contains values that have to be used to create <Route>. Since props name is ambiguous and is used within same component, it could be defined for explicitness as:
const RouteWithSubRoutes = routeProps => (
<Route
path={routeProps.path}
render={props => (
// pass the sub-routes down to keep nesting
<routeProps.component {...props} routes={routeProps.routes} />
)}
/>
);
Or with destructuring (component needs to be renamed to upper-case):
const RouteWithSubRoutes = ({ path, routes, component: Component }) => (
<Route
path={path}
render={props => (
<Component {...props} routes={routes} />
)}
/>
);
const RouteWithSubRoutes = ({route}) => (...) would be a mistake because routeProps object that RouteWithSubRoutes receives doesn't have route property.
In:
{routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
specifically in {...route}, you are not passing the component RouteWithSubRoutes a prop named route. By using the spread syntax (More here), you are passing every property of the routeobject as an individual prop. Thus, the first parameter of the render function does not actually contain a route object, it contains key and every one of the properties of route.
For example, if the route object looks like this:
{
something: "value",
somethingElse: "otherValue",
}
Then doing
{routes.map((route, i) => <RouteWithSubRoutes key={i} {...route} />)}
is equivalent to doing
{routes.map((route, i) => <RouteWithSubRoutes
key={i}
something={route.something}
somethingElse={route.somethingElse}
/>)}
What you actually seem to want to do is to:
{routes.map((route, i) => <RouteWithSubRoutes key={i} route={route} />)}
Where you pass your route object as the prop named route, allowing you to do:
const RouteWithSubRoutes = ({route}) => (
//THE RENDER OF THE ROUTE HERE
)
When you say:
As I know, this arrow function should get one parameter which we often call it as props, which should be a collection include all properties that be put in. In this case the props should include 'key' and all of properties of 'route'.
you are understanding how the spread syntax works. Props should include keyand all of the properties of route, and route does not include a property named route unless your actual routeobject contains inside another object named route, like so:
{
...
route: {
...
}
...
}

Passing props.children from a container component to a presentational component

I'm doing some routing, I want to try to display IndexZoomOverviewContainer into IndexZoomViewPanelContainer.
When I go to the right path "...index/overview", IndexZoomViewPanelContainer is displayed, but when I'm passing the children (the Route for IndexZoomOverviewContainer in this case) from the container (IndexZoomViewPanelContainer) to the view (IndexZoomViewPanelComponent), it doesn't display it and gives me an error:
Error ScreenShot : https://i.gyazo.com/990f92d3058806baa576dca5247ace9e.png
When I removed this.props.children, its not showing any error.
Here is the routing:
<Route className="fullHeight fullWidth" key="indexzoom" path="index/" component={indexmonitor.IndexZoomViewPanelContainer} >
<Route className="fullHeight fullWidth" key="indexzoom1" path="overview" component={indexmonitor.IndexZoomOverviewContainer} />
<Route className="fullHeight fullWidth" key="indexzoom2" path={routes.INDEX_ZOOM_CONSTITUENTS_RELATIVE_PATH} component={dashboard.DashboardListContainer} />
</Route>
IndexZoomViewPanelContainer:
class IndexZoomViewPanelContainer extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return <IndexZoomViewPanelComponent>
{this.props.children}
</IndexZoomViewPanelComponent>;
}
}
IndexZoomViewPanelComponent:
function IndexZoomViewPanelComponent(props) {
const tabs = getTabs();
return (
<div className="container">
<viewPanel.ViewPanel title={"Index Zoom"}
authKey={perm.INDEX_ZOOM_VIEWPANEL_PERM}
path={route.APP_PATH}
getPermStateFunc={(state) => state.MENUPERMS}
>
<TabControl tabs={tabs} selected={route.INDEX_ZOOM_OVERVIEW_RELATIVE_PATH}>
{props.children}
</TabControl>
</viewPanel.ViewPanel>
</div>
);
}
I want to try to display IndexZoomOverviewContainer into IndexZoomViewPanelContainer
<Route className="fullHeight fullWidth" key="indexzoom" path="index/"
component={indexmonitor.IndexZoomViewPanelContainer} >
IndexZoomViewPanelContainer renderd as a part of routing and it don't have any children
So,
return <IndexZoomViewPanelComponent>{this.props.children}</IndexZoomViewPanelComponent>
in the above script, this.props.children will be undefined and it will throw the error that you have shared.
In order to work this,it should be,
return <IndexZoomViewPanelComponent><IndexZoomOverviewContainer /></IndexZoomViewPanelComponent>
Here's a way to achieve what you need in the route :
// I'm renaming your components and removing className/key for readability
<Route path="/index" render={() => (
<IndexZoomViewPanelContainer>
<Route path="/index/overview" component={IndexZoomOverviewContainer} />
</IndexZoomViewPanelContainer>
)}
/>
You can also make your nested routes in IndexZoomViewPanelContainer component directly.
See https://reacttraining.com/react-router/core/api/Route/render-func for more infos.

Resources