When I import data-grid via lazy loading then the error came.
const DataGrid = lazy(async () => await import('#material-ui/data-grid'))
Please tell me whether I am importing correctly because when I import other material-ui components then that component works fine but for data-grid import it occurs an error.
DataGrid is not a default export, so try
const DataGrid = React.lazy(
() => import('#material-ui/data-grid').then(module => ({ default: module.DataGrid }))
);
React.lazy takes a function that must call a dynamic import(). This
must return a Promise which resolves to a module with a default export
containing a React component.
For me the below code works fine, based on answer from #Someone Special
import React, { lazy, Suspense } from 'react';
const loadable = (importFunc, { fallback = null } = { fallback: null }) => {
const LazyComponent = lazy(importFunc);
return props => (
<Suspense fallback={fallback}>
<LazyComponent {...props} />
</Suspense>
);
};
export default loadable;
import React, { Suspense } from 'react';
import { Loading } from 'dan-components';
import loadable from '../../utils/loadable';
const DataGrid = loadable(() =>
import('#material-ui/data-grid').then(module => {
return { default: module.DataGrid };
}),
);
const LazyDataGrid = props => {
return (
<Suspense fallback={<Loading />}>
<DataGrid {...props} />
</Suspense>
);
};
export default LazyDataGrid;
Related
I'm trying to import components with a delay.
I want to import the components quietly.
While looking at the home page, the back components will load silently.
I tried lazy loading, but this option returned me the page every time I logged in for a longer time.
const SignUp = lazy(async () => {
await new Promise(resolve => setTimeout(resolve, 5000));
return import('../sign-up/sign-up').then(({ SignUp }) => ({ default: SignUp }));});
How can I solve it?
thanks.
So there are three ways to lazy load component you can check my repo here https://github.com/amansadhwani/all-features-react/tree/master/src/components/LazyLoading
/* 1)Naive lazy load */
const Chart = lazy(() => import("./AnyComponent")); // this will load on demand when needed
/*2) Lazy load with prefetch */
const Chart = lazy(() => import(/* webpackPrefetch: true */ "./AnyComponent")); // this will load after everything is completed in short at the end of everything it will load
/*3) Predictive lazy load */
const loadComponent = () => import("./AnyComponent");
const Comp= lazy(loadComponent ); // this is something you will load as soon as someone hover's over any button you will attach event listners onFocus and onMouseEnter as shown below
onFocus={Comp}
onMouseEnter={Comp}
React.lazy() is a new function in react that lets you load react components lazily through code splitting without help from any additional libraries. Lazy loading is the technique of rendering only-needed or critical user interface items first , then quietly unrolling the non-critical items later.
Suspense is a component required by the lazy function basically used to wrap lazy components.
Solution for Using React Suspense (React 16.6+) version
import React, { Suspense } from "react";
const Customer = React.lazy(() => import("./Customer.js"));
const Admin = React.lazy(() => import("./Admin.js"));
//Instead of regular import statements, we will use the above approach for lazy loading
export default (props) => {
if (props.user === "admin") {
return (
// fallback component is rendered until our main component is loaded
<Suspense fallback={<div>Loading</div>}>
<Admin />
</Suspense>
);
} else if (props.user === "customer") {
return (
<Suspense fallback={<div>Loading</div>}>
<Customer />
</Suspense>
);
} else {
return <div> Invalid User </div>;
}
};
Without React Suspense
Our calling component, in this case Home.js
import React from "react";
import { lazyLoader } from "./lazyLoader";
const Customer = lazyLoader(() => import("./Customer.js"));
const Admin = lazyLoader(() => import("./Admin.js"));
//Instead of regular import statements, we will use the above approach for lazy loading
export default (props) => {
if (props.user === "admin") {
return <Admin />;
} else if (props.user === "customer") {
return <Customer />;
} else {
return <div> Invalid User </div>;
}
};
And lazyLoader.js
const lazyLoader = (importComp, fallback) => {
return class extends React.Component {
state = {
component: null, //initializing state
};
//loading the component and setting it to state
componentDidMount() {
importComp().then((comp) => setState({ component: comp.default }));
}
//rendering the component
render() {
const C = this.state.component;
return C ? (
<C {...this.props} />
) : fallback ? (
fallback
) : (
<div>loading</div>
);
// If component is not loaded then return fallback component, if fallback is not provided then use default loading
}
};
};
export default lazyLoader;
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'] }
I have an issue when i try to use functions from a context inside a child component in a React native android app.
Below is my code for the context, and the form component im using it in (stripped down for brevity).
The "isFormOpen" object can be read no problem from inside any children that is wrapped in the provider, but when i try to call the "toggleForm" function from the same child component, it does nothing, no console errors either.
I have another context which is identical in structure and syntax except for vairable and function names etc, and that works perfectly, so im a bit confused as to why this does not work. I removed the other context, thinking there might be some type of conflict, but didnt solve it.
AccountContext.tsx
import React, { FC, createContext, useContext, useState } from 'react';
interface AccountContextType {
isFormOpen: boolean,
toggleForm: (toggle: boolean) => void
};
export const AccountContext = createContext<AccountContextType>({
isFormOpen: false,
toggleForm: () => null
});
export const AccountContextProvider: FC = props => {
const [formOpen, setFormOpen] = useState<boolean>(false);
const toggleForm = (toggle: boolean) => {
setFormOpen(toggle);
}
const value: AccountContextType = {
isFormOpen: formOpen,
toggleForm
}
return (
<AccountContext.Provider value={value}>
{props.children}
</AccountContext.Provider>
)
}
export const useAccountContext = () => useContext(AccountContext);
TrackUploadForm.js
import React from 'react';
import { SafeAreaView } from 'react-native';
import { Button } from 'react-native-paper';
import { useAccountContext } from '../contexts/AccountContext';
import { AccountContextProvider } from '../contexts/AccountContext';
const TrackUploadForm = () => {
const accountContext = useAccountContext();
return (
<AccountContextProvider>
<SafeAreaView>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</SafeAreaView>
</AccountContextProvider>
)
};
export default TrackUploadForm;
useAccountContext is called outside the provider
export default function App() {
return (
<AccountContextProvider>
<Content />
</AccountContextProvider>
);
}
const Content = () => {
const accountContext = useAccountContext();
return (
<div className="App">
<h1>{accountContext.isFormOpen ? "true" : "false"}</h1>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</div>
);
};
accountContext.toggleForm(false) <-- always false, change it to accountContext.toggleForm(!accountContext.isFormOpen)
Together we have
https://codesandbox.io/s/cranky-panini-yo129
I've created a custom context hook - and I'm struggling to figure out how to pass values to its provider during testing.
My hook:
import React, { createContext, useContext, useState } from 'react';
const Context = createContext({});
export const ConfigurationProvider = ({ children }) => {
// Use State to keep the values
const [configuration, setConfiguration] = useState({});
// pass the value in provider and return
return (
<Context.Provider
value={{
configuration,
setConfiguration,
}}
>
{children}
</Context.Provider>
);
};
export const useConfigurationContext = () => useContext(Context);
export const { Consumer: ConfigurationConsumer } = Context;
This is how it's used in the application:
function App() {
return (
<ConfigurationProvider>
<div className="app">
<ComponentA />
</div>
</ConfigurationProvider>
);
}
And in ComponentA:
const ComponentA = () => {
// Get configuration
const configuration = useConfigurationContext();
return (
<div>{JSON.stringify(configuration)}</div>
)
}
This all works fine - considered that I'm calling setConfiguration from another component and set an object. Now for the testing part:
import React, { Component, createContext } from 'react';
import { render, waitFor } from '#testing-library/react';
import ComponentA from 'componentA';
const config = {
propertyA: 'hello',
};
test('renders the config', async () => {
const ConfigurationContext = createContext();
const { queryByText } = render(
<ConfigurationContext.Provider value={config}>
<ComponentA />
</ConfigurationContext.Provider>
);
expect(queryByText('hello')).toBeInTheDocument();
});
This doesn't work - I'm expecting the value that I'm sending in would be rendered in the div, but the context is an empty object. What am I doing wrong?
Thanks to Carle B. Navy I got the reason why it doesn't work. For other people two wonder what the solution is I fixed it by doing the following:
In my context hook, I changed the last line to export the provider as well:
export const { Consumer: ConfigConsumer, Provider: ConfigProvider } = Context;
Then in my test case, instead of creating a new context, I import the ConfigProvider at the top, and then:
const { queryByText } = render(
<ConfigProvider value={config}>
<ComponentA />
</ConfigProvider>
);
Thanks for helping me solve this and hope this helps someone else.
I am trying to use dynamic import in NextJS and I do not understand why it works only while storing imported component in a variable. It breaks when I try to return it from other function.
It works this way:
import dynamic from "next/dynamic";
const Article = dynamic(() => import("tutorial/ru/welcome.mdx"));
but like this, well, it breaks:
import dynamic from "next/dynamic";
export default ({ route }) => {
switch (route) {
case "ru":
default:
return dynamic(() => import("tutorial/ru/welcome.mdx"));
}
};
I get the Invalid hook call. Hooks can only be called inside of the body of a function component message.
I think you need to export it , then try to use it like so :
import dynamic from "next/dynamic";
const Article = dynamic(() => import("tutorial/ru/welcome.mdx"));
export default Article;
then try to use it in switch statement :
import Article from './Article';
export default ({ route }) => {
switch (route) {
case "ru":
return (<></>)
default:
return <Article />;
}
};
I found a solution to get over this issue!
import dynamic from "next/dynamic";
import Loader from "components/Loader/Loader";
import Error from "pages/_error";
export default ({ route }) => {
const Article = dynamic(
() => import(`tutorial/${route}.mdx`).catch(err => {
return () => <Error />
}),
{ loading: () => <Loader /> }
);
return <Article />
};
I should store the component in the variable after all, but I get the component itself dynamically using literal strings, and after that I return the component as tag (). Works fine now!