Next.js static generation with dynamic imports - javascript

I have a Template component which builds a page based on a json structure. The json contains multiple objects including keys, which then define what component is to be used.
Like this:
const componentList: Record<string, any> = {
BlockCards: dynamic<Data.BlockCards>(() =>
import("../molecule/BlockCards").then(module => module.BlockCards)
),
BlockNews: dynamic<Data.BlockNews>(() =>
import("../molecule/BlockNews").then(module => module.BlockNews)
),
PageHeader: dynamic<Data.PageHeader>(() =>
import("../atom/PageHeader").then(module => module.PageHeader)
),
};
interface TemplateProps {
data: Data.Main;
}
export const Template: React.FC<TemplateProps> = ({ data }) => {
return (
<React.Fragment>
{data.content.map((item, index) => {
const Component = componentList[item.component.type];
if (!Component) return null;
return (
<Block key={index}>
<Component {...item.component} {...props} />
</Block>
);
})}
</React.Fragment>
);
};
This works, but the components are lazy loaded on the client's side. I want the rendering to happen on build, just like with getStaticProps. Importing my components without using dynamic works and the components will be prerendered thanks to next's static optimization. But since this one Template components imports multiple components that might not be used by some pages, I need the components to be only imported if used - how can I do so?

Related

React -- Trigger chart download method from sibling component

Looking for some guidance on how to structure the below components to trigger an (export) method in one component from another 'sibling' component.
Using a download button contained in <DownloadChartButton> in the Card Header, I want to download a chart contained in my PaginatedRechartsChart component as a PNG. I currently have the components structured like below. DownloadChartButton needs to access the recharts chart contained in my <PaginatedRechartsChart> component. Hoping to keep DashboardCard, DownloadChartButton, and PaginatedRechartsChart generic so they can be applied in many places, hence the current structure.
Question: Is there a better way to structure my components to avoid the React anti-pattern of triggering child methods from parent components?
Thank you for your help!
Pseudo-code:
export const LineChartByDay = () => {
const [data, setData] = React.useState([{'date':'2021-01-01': 'count':34})
return (
<DashboardCard
headerItems={<DownloadChartButton data={data}/>}
>
<PaginatedRechartsChart data={data}/>
</DashboardCard>
)
}
import {LineChart} from 'recharts'
export const PaginatedRechartsChart = (data) => {
// ... extra code for client-side pagination, etc.
return (
<ResponsiveContainer>
<LineChart data={data}/>
</ResponsiveContainer>
)
}
export const DashboardCard = ({ title, actions, children, error, loading }: { title: string, actions?: React.ReactNode, children?: React.ReactNode, error?: any, loading?: boolean }) =\> {
return (
<Card>
<CardHeader
title={title}
action={actions}
></CardHeader>
<CardContent>
{error && <ErrorAlertBar title={error}/>}
{loading && <CircularProgress/>}
{children}
</CardContent>
</Card>
)
}
export const DownloadChartButton = (data) => {
// ... CSV download logic (takes a list of objects in data, exports to CSV)
// ... PNG download logic
const [getPng, { ref }] = useCurrentPng();
//Ideally we'd have chart download logic in this component so we don't have to copy it into every chart component
const handleDownload = React.useCallback(async () => {
const png = await getPng();
// Verify that png is not undefined
if (png) {
// Download with FileSaver
FileSaver.saveAs(png, 'myChart.png');
}
}, [getPng]);
return (
<MenuButton>
<MenuItem>CSV</MenuItem>
<MenuItem>PNG</MenuItem>
</MenuButton>
)
}
From what I see online, my current thought is using a ref in the parent component and passing that to PaginatedRechartsChart and DownloadChartButton... but I'm seeing online that this is an anti-pattern for React (i.e. https://stackoverflow.com/a/37950970/20391795)

How to make a Wrapper or Layout to avoid repetition on several NextJS pages

I've been working with Nextjs for a while and I'm trying to avoid repetitive tasks. For a dynamic site, the menu and footer which are repeated on the whole page cause me a serious problem. To avoid this, I thought of making a Wrapper that I repeat on all the pages and pass the data in props.
But what I'm doing is making a wrapper that loads the data and won't be repeated on all the pages.
Look at the picture to get an idea of what I say
https://www.figma.com/file/u4okWHg3CpbgLPpZEiYJxI/Untitled?node-id=0%3A1
Layout and wrapper are two different things.
BaseLayout used to share a common section across multiple pages.
const BaseLayout= (props) => {
const {
className,user,navClass,loading,children,noSideBar,} = props;
// Define renderer to render the view
const renderer = () => (
<div className={`baselayout ${className}`}>
{noSideBar ? "" : <SideBar />}
<main>
{/* Prop drilling here */}
<Header className={navClass} user={user} loading={loading} />
{children}
<Footer />
</main>
</div>
);
return <> {renderer()} </>;
};
export default BaseLayout;
Then you use it inside a page
const AboutMe = () => {
return (
<BaseLayout user={data} loading={loading}>
{/* YOUR PAGE COMPONENT HERRE */}
</BaseLayout>
);
};
Wrappers are used to create and support basic UI elements based on the data or prop values. These wrapper contain an instance of another class or component and provide a new way to use an object. For example provide pages to an admin
function withAuth(Component) {
return (role) => {
return (props) => {
// an hook to get the props. based on this we decide what to render
const { data, loading } = useGetUser();
if (loading) {
return <p>Loading...</p>;
}
if (!data) {
return <Redirect to="/api/v1/login" />;
} else {
// if not authorized redirect to login
if (role && !isAuthorized(data, role)) {
return <Redirect to="/api/v1/login" />;
}
// if it is authorized return compoentn
return <Component user={data} loading={loading} {...props} />;
}
};
};
}
export default withAuth;
Then maybe we have a page for only admin
const OnlyAdmin = ({ user, loading }: { user: IUser; loading: true }) => {
return (
<BaseLayout user={user} loading={loading}>
<h1>I am Admin Page - Hello {user.name}</h1>
</BaseLayout>
);
};
// we are wrapping the component to pass a new property
export default withAuth(OnlyAdmin)("admin");
What I've done for that situation is have a context that gets the header and footer data client side and store it as JSON in window.sessionStorage. Then your header and footer are only loaded when blank or when invalidated by 'something' e.g certain routes or user login status changes.
Keeps things very fast and avoids needless delays. If you are going for full SSR you could do the same thing with server side sessions.

How to reduce render time while rendering huge list of items in Ionic React

I am using IonicReact to build a mobile app. I just wanna render a list of items from a local JSON file (JSON file contains an array of more than 1000 items). I map through the array items and print them by wrapping them in <IonItem>.It takes few seconds to iterate through all the items and displaying them in the browser. Please help me, how do I optimize render time, load the data fast from JSON.
Code is below:
//necessary imports
import data from './data.json';
const ListItems: React.FC = () => {
const showItems = data
.map((topic) => {
return (<IonItem>{topic}</IonItem>);})
return (
<IonPage>
<IonContent fullscreen className="bg-style">
<IonList>{showItems}</IonList>
</IonContent>
</IonPage>
);
};
export default ListItems;
Basically it should be two methods: 1. virtual scroll. 2. infinite scroll by cutting your large array to small chunks.
However, in your case, the virtual scroll method should be much better.
Ionic virtual scroll doesn't support react. But I found an article and reimplemented the VirtualScrollChild component:
https://medium.com/#alvinnguyen116/virtual-and-infinite-scrolling-in-react-d56a05976cd2
import React from "react"
import { useInView } from "react-intersection-observer"
interface Props {
height?: number
}
export const VirtualScrollChild: React.FC<Props> = (props) => {
const [ref, inView] = useInView()
const style = {
height: `${props.height ? props.height : 40}px`,
overflow: "hidden",
}
return (
<div style={style} ref={ref}>
{inView ? props.children : null}
</div>
)
}
usage:
<IonList>
{your_data_list.map((data) => (
<VirtualScrollChild height={your_height}>
{data...}
</VirtualScrollChild>
))}
</IonList>

How to efficiently pass props in nested components?

Say there are nested components, where data is passed from top to root, but only root component needs the data, how to deal with this more efficiently? Check the following example:
const Myform = () => {
return (
<Section title={'Nested component'} />
)
}
const Section = ({title}) => {
return (
<Card title={title} />
)
}
const Card = ({title}) => {
return (
<Title title={title} />
)
}
const Title = ({title}) => {
return (
<h2>{title}</h2>
)
}
Myform passes title data to Section, then passes to Card, then passes to root component Title, where data is only needed in Title component. The 2 middle components only pass data. I can see two problems:
Each middle component needs to accept title, but has no use of title at all;
If more middle components are needed, each of them needs to repeat the same process.
In fact only Myform and Title need to know the data. Is there a more efficient way to deal with this kind of scenario?
You can create the Title component at the top and pass that down instead of the props. This is what Facebook recommends here in their docs on Composition vs Inheritance
EG:
const Myform = () => {
return (
<Section Title={<Title title='Nested component' />} />
)
}
const Section = ({Title}) => {
return (
<Card Title={Title} />
)
}
const Card = ({Title}) => {
return (
<Title />
)
}
const Title = ({title}) => {
return (
<h2>{title}</h2>
)
}
In this example with so many levels it doesn't work that great - in that case the Context API might be better

How to write a wrapper around a material UI component using React JS?

I am using Material UI next library and currently I am using List component. Since the library is in beta, lot of its parameter names get changed. To solve this I am planning to write a wrapper around the required components so that things wont break. My list component :
<List dense>
<List className={classes.myListStyles}>
<ListItem disableGutters/>
</List>
</List>
How should I write the wrapper for the List(say myListWrapper) and ListItem so that the wrapper component can handle props and pass them to the actual MUI list component inside?
I had worked on MUI wrappers, writing my own library for a project. The implementation we are focusing, is to pass the props to inner/actual-MUI component from the our wrapper component. with manipulation. In case of wrapping props for abstraction.
Following is my approach to the solution:
import { List as MaterialList } from 'material-ui/List';
import { React } from 'react';
import { ListItem as MaterialListI } from 'material-ui/ListItem';
class List extends MaterialList {
constructor(props){
const propsToPass = {
prop1 : change(props.prop1),
...props
}
super(propsToPass);
}
};
class ListItem extends MaterialListItem {
const propsToPass = {
prop1 : change(props.prop1),
prop2 : change(props.prop2),
...props
}
super(propsToPass);
}
};
class App extends React.Component {
render () {
return (
<List prop='value' >
<ListItem prop1={somevalue1} prop2={somevalue2} />
<ListItem prop1={somevalue1} prop2={somevalue2} />
<ListItem prop1={somevalue1} prop2={somevalue2} />
</List>
)
}
};
Above code will allow following things to do with your component:
You can use the props with exact names, as used in Material UI.
You can manipulate/change/transform/reshape you props passed from outside.
If props to you wrapper components are passed with exactly same names as MUI is using, they will directly be sent to the inner component. (... operator.)
You can use Component with exact same name as material is using to avoid confusion.
Code is written according to advance JSX and JavaScript ES6 standards.
You have a space to manipulate your props to pass into the MUI Components.
You can also implement type checking using proptypes.
You can ask for any confusion/query.
You can write it like this:
const MyList = props => (
<List
{/*mention props values here*/}
propA={props.A}
propB={props.B}
>
{props.children}
</List>
)
const MyListItem = props => (
<ListItem
{/*mention props values here*/}
propA={props.A}
propB={props.B}
>
{props.children}
</ListItem>
)
Now you need to use MyList and MyListItem, decide the prop names for these component (as per your convenient), and inside these component map those values to actual Material-UI component properties.
Note:
If you are using the same prop names (same name as material-ui component expect) for your component then you can write like this also:
const MyList = ({children, ...rest}) => <div {...rest}>{children}</div>
const MyListItem = ({children, ...rest}) => <p {...rest}>{children}</p>
Check this example:
const A = props => <div>{props.children}</div>
const B = props => <p>{props.children}</p>
ReactDOM.render(
<A>
<A>
<B>Hello</B>
</A>
</A>,
document.getElementById('app')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app' />

Categories

Resources