Material UI tabs navigation
Unanswered
Acacia-ants posted this in #help-forum
Acacia-antsOP
Hey guys. I have a strange problem with our webapp, I have a screen with material ui tabs. Normally switching between them don't take too much time but when I open one of the modals which stays on screen when changing tabs it's taking long time. Take a look on videos. Do you have any idea what's wrong here?
86 Replies
Material hi is heavy code wise. You’re going to have to provide code to get assistance.
Acacia-antsOP
Hey thanks for answering. I digged in a little bit in. The problem is that one part of modal is using few components with react context. When I comment this part it start to working fine
Do you have any idea why it can cause a problem?
this whole part
when I comment out this part like this
the problem is still appearing
in CategoriesWatcher I've got this code
import Loading from "@app/loading";
import { getCaseTypeAndCategories, useAppSelector } from "@app/redux";
import { getCategoryDetails } from "@app/settings/categories/(common)/actions";
import { CategoryAddEdit, Maybe } from "@app/types";
import { hookFactory, useServerQuery } from "@app/utils/hooks";
import React from "react";
import { getCategoryId } from "../form-state/helpers";
import { AdditionalDetailsWatcher } from "../additional-details-watcher/additional-details-watcher";
type CategoryWatcherCtx = {
isPending: false;
data: Maybe<CategoryAddEdit>;
};
const ctx = React.createContext<CategoryWatcherCtx>({
data: null,
isPending: false
});
export const useCategoriesWatcher = hookFactory({
ctx,
hookName: "useCategoriesWatcher",
providerName: "CategoriesWatcher"
});
type CategoriesWatcherProps = {
children: React.ReactNode;
renderLoader?: () => JSX.Element;
};
export const CategoriesWatcher = ({ children, renderLoader = () => <Loading /> }: CategoriesWatcherProps) => {
const caseTypeAndCategories = useAppSelector(getCaseTypeAndCategories);
const { data, isPending } = useServerQuery<CategoryAddEdit>({
action: async () => {
if (!caseTypeAndCategories) return;
const categoryId = getCategoryId(caseTypeAndCategories);
return getCategoryDetails(categoryId);
}
});
if (isPending) {
return renderLoader();
}
return (
<AdditionalDetailsWatcher additionalDetailsTabs={data?.additionalDetailsTabs || []}>
<ctx.Provider value={{ data, isPending }}>{children}</ctx.Provider>
</AdditionalDetailsWatcher>
);
};and this AdditionalDetailsWatcher
Lewis's Woodpecker
tip: use
```js
<code_here>
```
to have syntax highlighting
```js
<code_here>
```
to have syntax highlighting
Acacia-antsOP
yeah yeah I found out thanks 😄
what is funny, when I replace data response in CategoriesWatcher to empty object {}, remove loader etc AND in AdditionalDetailsWatcher I will also pass empty value to Provider it's still the same
Did you try memoizing the component?
Acacia-antsOP
Yeah I memoized these values passed to context
@Acacia-ants when I comment out this part like this
Acacia-antsOP
ok I already sent these two components so let me show you full code and you will catch something
Are you fetching something on these tabs ?
Acacia-antsOP
yep of course
but without opened modal It's like instan redirect
Do you keep values in a store ?
Acacia-antsOP
redux?
not necessarily
it can be any of store lib or react context
Acacia-antsOP
Actually each tab is page.tsx where I'm using server action to fetch data = one or two requests
and passing them to client components where I have UI etc
I guess your fetch taking time and thats why
Acacia-antsOP
ok but why when I don't have modal is totally fine
even when I open other modal (I have few of them) it's totally fine
let me record some video quickly
Acacia-antsOP
Yeah yeah
Uhm there is another issue there. When your modal isnt open and you're switching you have a fallback display (spinner loading)
Acacia-antsOP
without this particular modal it's totally fine, I opened others and tried to switch tabs and redirect is instant and than I can see loading for them
when your modal is open you dont have it
Acacia-antsOP
I have it, let me record one thing
look, when I have modal opened and trying to switch tab, it causing a freeze
on Contact Details tab it's a freeze + quick request
Can you share the project structure please ?
Acacia-antsOP
but on Links I have some more data to fetch so it showing loading idicator after that freeze
what do you exactly need?
Acacia-antsOP
So I have a page folder called case-details/[id] inside of it I have layout.tsx like this
"use client";
import Box from "@mui/material/Box";
import React from "react";
import { CaseOptionDialogs } from "./components/case-option-dialogs";
import { CategoriesTabs } from "./components/categories-tabs";
import { NotesGrid } from "./details/notes-grid";
const CategoriesLayout = ({ children }: { children: React.ReactNode }) => {
return (
<>
<Box sx={{ width: "100%" }}>
<CategoriesTabs />
<CaseOptionDialogs />
{children}
<NotesGrid />
</Box>
</>
);
};
export default CategoriesLayout;Let me just copy paste your code to have code style its more lisible
import Loading from "@app/loading";
import { getCaseTypeAndCategories, useAppSelector } from "@app/redux";
import { getCategoryDetails } from "@app/settings/categories/(common)/actions";
import { CategoryAddEdit, Maybe } from "@app/types";
import { hookFactory, useServerQuery } from "@app/utils/hooks";
import React from "react";
import { getCategoryId } from "../form-state/helpers";
import { AdditionalDetailsWatcher } from "../additional-details-watcher/additional-details-watcher";
type CategoryWatcherCtx = {
isPending: false;
data: Maybe<CategoryAddEdit>;
};
const ctx = React.createContext<CategoryWatcherCtx>({
data: null,
isPending: false
});
export const useCategoriesWatcher = hookFactory({
ctx,
hookName: "useCategoriesWatcher",
providerName: "CategoriesWatcher"
});
type CategoriesWatcherProps = {
children: React.ReactNode;
renderLoader?: () => JSX.Element;
};
export const CategoriesWatcher = ({ children, renderLoader = () => <Loading /> }: CategoriesWatcherProps) => {
const caseTypeAndCategories = useAppSelector(getCaseTypeAndCategories);
const { data, isPending } = useServerQuery<CategoryAddEdit>({
action: async () => {
if (!caseTypeAndCategories) return;
const categoryId = getCategoryId(caseTypeAndCategories);
return getCategoryDetails(categoryId);
}
});
if (isPending) {
return renderLoader();
}
return (
<AdditionalDetailsWatcher additionalDetailsTabs={data?.additionalDetailsTabs || []}>
<ctx.Provider value={{ data, isPending }}>{children}</ctx.Provider>
</AdditionalDetailsWatcher>
);
};Acacia-antsOP
{children} is loading each page.tsx inside this main page folder yeah
Can you share your main layout ?
Acacia-antsOP
you mean this highest one in hierarchy
Well not needed i guess
@Acacia-ants So I have a page folder called case-details/[id] inside of it I have layout.tsx like this "use client";
import Box from "@mui/material/Box";
import React from "react";
import { CaseOptionDialogs } from "./components/case-option-dialogs";
import { CategoriesTabs } from "./components/categories-tabs";
import { NotesGrid } from "./details/notes-grid";
const CategoriesLayout = ({ children }: { children: React.ReactNode }) => {
return (
<>
<Box sx={{ width: "100%" }}>
<CategoriesTabs />
<CaseOptionDialogs />
{children}
<NotesGrid />
</Box>
</>
);
};
export default CategoriesLayout;
Acacia-antsOP
this one is categories layout so it's actually a layout for this page
there is another one global layout for header, footer, sidebar menu and this main content section where I'm rendering a content of course
Whats the categories watcher and Links state provider ?
Acacia-antsOP
and this is an example tab/page, others are similar 1-2 server actions there
import Grid from "@mui/material/Grid";
import { getCaseLinks } from "../(common)/actions";
import { LinksDetails } from "./details";
type DetailsProps = {
params: {
id: number;
};
};
export default async function Details({ params }: DetailsProps) {
const id = params?.id;
const linkDetails = await getCaseLinks(id);
return (
<Grid container spacing={2} justifyContent="flex-end">
<Grid item xs={12}>
<LinksDetails linksDetails={linkDetails} />
</Grid>
</Grid>
);
}there is a logic in our application where we are displaying these links based on created item, it's for bussiness implementation
you need to have X to be able to see Link A, Link B etc
you know what I mean
all files used in these manage-links modal
Which file is used for modal?
Acacia-antsOP
Components named like in files which I sent
The issue is coming from your Modal and how its shared. You may investigate on this
Acacia-antsOP
what do you mean?
Dialog component and grid component
Your issue is only when modal is open right ?
Acacia-antsOP
but you know, I was testing it and commented out this whole strucate with CategoryWatcher and it started working fine
yeah
@Acacia-ants but you know, I was testing it and commented out this whole strucate with CategoryWatcher and it started working fine
if that was working fine the issue may come from other provider then
I cannot help much I'm a bit lost in your code :/
Acacia-antsOP
ok give me a minute I will try to do one more thing
Acacia-antsOP
ok I have an update
const caseTypeAndCategories = useAppSelector(getCaseTypeAndCategories);export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;redux useSelector
any idea how to take care of it?
Acacia-antsOP
@ItetsuLaTable
is getCaseTypeAndCategories a function ?
because its seems like a function but you're not calling it with ()
Acacia-antsOP
export const getCaseTypeAndCategories = (state: RootState) => state.addCaseReducer.caseTypeAndCategorySelection;it's a getter function
Acacia-antsOP
I tried to memoize it somehow but still the same
export const CategoriesWatcher = ({ children, renderLoader = () => <Loading /> }: CategoriesWatcherProps) => {
const caseTypeAndCategories = useAppSelector(getCaseTypeAndCategories);
const categoryId = useMemo(() => {
if (!caseTypeAndCategories) return null;
return getCategoryId(caseTypeAndCategories);
}, [caseTypeAndCategories]);
const { data, isPending } = useServerQuery<CategoryAddEdit>({
action: async () => {
if (!categoryId) return;
return getCategoryDetails(categoryId);
},
deps: [categoryId]
});
if (isPending) {
return renderLoader();
}
return (
<AdditionalDetailsWatcher additionalDetailsTabs={data?.additionalDetailsTabs || []}>
<ctx.Provider value={{ data, isPending }}>{children}</ctx.Provider>
</AdditionalDetailsWatcher>
);
};