Quantcast
Channel: Active questions tagged tabs - Stack Overflow
Viewing all articles
Browse latest Browse all 587

React Web FF DevTools Profile Flamegraph "Why did this render?" has "The parent component rendered", but Flamegraph does not show activated parent?

$
0
0

I am developing React web SPA ERP application with dynamic mulitabbed interface and I am having problems with extra-rerenderings. I am thinking about different options (e.g. my question about component design https://softwareengineering.stackexchange.com/questions/455372/singe-page-application-for-the-web-with-dynamic-tabbed-interface-is-there-ne/455375) and I am doing profiling of my code and I have arrived at this situation: re-render due to re-render of parent, but there is no parent in Flamegraph!

My application is: the user can open new in-app tabs, each new form is opened as respective component-view in its own tab. I tested my application: I opened my application with the default Dashboard tab visible then I clicked on the link and opened other view in its onw in-application tab and then I switched back to the Dashboard tab and back to the new tab. TabPanel (key=0) is the TabPanel component that hosts Dashboard component.

So - clicking on the link, swithcing between tabs - all this is causing url changes (for the same domain, of course, this if SPA app after all) and url changes are processed in a manner that causes changes to the data (tabs array and activeTabId variable) and data ar rendered into Tab's and TabPanel's.

Of course - each url change and activeTabId may cause and causes re-renders, including re-render of TabPanel key=0, that is OK. But the strange situation is, that after each re-render due to url (react-router-dom location) change and re-render after activeTabId change, there are 4 re-renders of TabPanel key=0 due to "Why did this render?"="The parent component rendered" - see picture. And the Flamegraphs shows no re-rendered parent for any of those 4 re-rerenders! This is really strange. How the component may be re-rendered due to parent re-render, if there is no re-rendered parent. I have checked the parent re-renders and they count was minimal. Can ir be possible that for one parent re-render (e.g. due to url/location change or activeTabId change) there can be following 4 extra re-renders of the children to the parent?

I am giving the full code of the minimal app (I summarized it from my production app but I have not tested this summarized app, it illustrates my approach: 1) any view open or switching between tabs goes through url changes, 2) that are handled by the router, saved into data and 3) Tab's/TabPanel's are rendered from those data dynamically) at the end, but here is the crux of rendering of TapPabel's - maybe this rendering is somehow inefficient and introduces parent problems?

  {tabs.map((tab) => (<TabPanel key={tab.id} tab_hidden={activeTabId !== tab.id}>      {tab.component}</TabPanel>  ))}

Here is one-pager with full (summarized, essential code):

import React, { useState, useEffect, useCallback, useMemo } from 'react';import { BrowserRouter, Routes, Route, useNavigate, useLocation } from 'react-router-dom';import { Tabs, Tab, Box } from '@mui/material';import CloseIcon from '@mui/icons-material/Close';// Closable Tab componentconst ClosableTab = React.memo(({ label, onClose, closable }) => (<Box sx={{ display: 'flex', alignItems: 'center' }}><span>{label}</span>    {closable && (<CloseIcon        onClick={(e) => {          e.stopPropagation();          onClose();        }}        sx={{ ml: 1, cursor: 'pointer', fontSize: 'small' }}      />    )}</Box>));// TabPanel componentconst TabPanel = React.memo(({ children, tab_hidden }) => (<div role="tabpanel" hidden={tab_hidden} style={{ display: tab_hidden ? 'none' : 'flex', flexDirection: 'column', flex: 1 }}><Box sx={{ p: 3 }}>{children}</Box></div>));// Create a new tabconst createTab = (id, path, label, component, closable = true) => ({  id,  path,  label,  component,  closable,});const App = () => {  return (<BrowserRouter><MainApp /></BrowserRouter>  );};// MainApp Component with Tab and Routing Logicconst MainApp = () => {  const navigate = useNavigate();  const location = useLocation();  const [tabs, setTabs] = useState([    createTab(0, '/dashboard', 'Dashboard', <DashboardView />, false),  ]);  const [activeTabId, setActiveTabId] = useState(0);  useEffect(() => {    const currentPath = location.pathname;    const foundTab = tabs.find((tab) => tab.path === currentPath);    if (foundTab) {      setActiveTabId(foundTab.id);    }  }, [location.pathname, tabs]);  const handleTabChange = (event, newValue) => {    const selectedTab = tabs.find((tab) => tab.id === newValue);    if (selectedTab) {      navigate(selectedTab.path);    }    setActiveTabId(newValue);  };  const addTab = (path, label, component) => {    const newId = tabs.length;    const newTab = createTab(newId, path, label, component);    setTabs((prev) => [...prev, newTab]);    setActiveTabId(newTab.id);    navigate(path);  };  const closeTab = (tabId) => {    setTabs((prev) => prev.filter((tab) => tab.id !== tabId));    setActiveTabId((prev) => (prev === tabId ? 0 : prev)); // Switch to Dashboard if closing active    navigate('/dashboard');  };  const memoizedTabs = useMemo(    () =>      tabs.map((tab) => (<Tab          key={tab.id}          value={tab.id}          label={<ClosableTab label={tab.label} onClose={() => closeTab(tab.id)} closable={tab.closable} />}        />      )),    [tabs]  );  return (<Box sx={{ width: '100%', display: 'flex', flexDirection: 'column', flex: 1, overflow: 'hidden' }}><Tabs value={activeTabId} onChange={handleTabChange} variant="scrollable" scrollButtons="auto">        {memoizedTabs}</Tabs>      {tabs.map((tab) => (<TabPanel key={tab.id} tab_hidden={activeTabId !== tab.id}>          {tab.component}</TabPanel>      ))}<Box sx={{ p: 2 }}><button onClick={() => addTab('/tab1', 'Tab 1', <ExampleView id={1} />)}>Open Tab 1</button><button onClick={() => addTab('/tab2', 'Tab 2', <ExampleView id={2} />)}>Open Tab 2</button></Box>      {/* Routes for different views */}<Routes><Route path="/dashboard" element={<DashboardView />} /><Route path="/tab1" element={<ExampleView id={1} />} /><Route path="/tab2" element={<ExampleView id={2} />} /></Routes></Box>  );};// Example viewsconst DashboardView = React.memo(() => (<div><h2>Dashboard</h2><p>This is the dashboard view.</p></div>));const ExampleView = React.memo(({ id }) => (<div><h2>Tab {id}</h2><p>This is content for tab {id}.</p></div>));export default App;

Viewing all articles
Browse latest Browse all 587

Latest Images

Trending Articles



Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>