I set up a minimal Gatsby page to test when Gatsby re-renders, and I found out that just clicking a regular in-route (hash) anchor link, causes a re-render.
Why is that? Is there any way to prevent it?
Here is an example page:
const SomePage = props => {
console.log('RE-RENDERING PAGE');
return (
<>
Link that should not fire re-render;
</>
);
};
import { Link } from "gatsby"
const SomePage = props => {
return (
<Link to="#foo">Link that should not fire re-render</Link>;
);
};
<Link> will render a fully accessible anchor tag with the proper href.
React re-renders a lot. Since the entire page is wrapped in a Reach Router and you're using a non-memo functional component I'm not the slightest bit surprised you're getting a console message. I was under the impression you were trying to avoid a page reload, not prop-change-based React render.
If you'd like to prevent the re-render, you can use React.memo:
const SomePage = React.memo(() => <Link to="#foo">Text</Link>)
Related
I developed a simple navbar that has a JSON as data to dynamically construct its links. Visually, I need to persist the navbar with the current active link/route. I tested out two ways:
First implementation:
Menu component:
const ToggleMenuHandler = (e: React.MouseEvent<HTMLElement>) => {
clearMenu();
activateItem(e.currentTarget);
e.stopPropagation();
}
const MenuItems = MenuContent.map(item => {
return (
<NavigationItem key={uuidv4()} {...item} clickHandler={ToggleMenuHandler} />
);
});
Navigation item component:
<ListItem onClick={(event: React.MouseEvent<HTMLElement>) => props.clickHandler(event)}>
<Link href={sub.href}>{sub.name}</Link>
</ListItem>
I'm sure that ToggleMenuHandler function is working properly after debugging it. activateItem is the function responsible for styling an item as active. If I remove the <Link> component from the navigation item, it works just fine. So I figured my menu component was being remounted, which led me to the second implementation.
Second implementation
This one was based on the principle that my menu was being remounted. What I did was to bind the href property of <Link> to the NavigationItem's id. That way, by using router.pathname I could find the item that should be activated, and the code menu component changed to:
const router = useRouter();
useEffect(() => {
activateItem(document.querySelector(`#${router.pathname}`))
}, []);
...and I also removed the ToggleMenuHandler from the menu item component. That way, when the page was redicted I would get its path and, with it, find the list item to activate visually. I also debbug the useEffect hook and it was working properly. However, it was not activated when the Link was clicked.
Question:
What exactly is triggered when a NextJS Link component is clicked, and given the context of this question, how can I handle that event?
I don't really know how the Next.js Link component works but I know that it does not need a click event handler.
Also, there should be an <a> tag inside the Link component to work properly as a link according to the docs.
I also have personally experienced if we use another element inside the Link component, it does not work like a link but navigates.
I got an issue in my project , that I had a main component which acts as the parent component of my project inside that I had the routes for the other components and some other component which are directly imported into it like a Side Navbar Component and Login Component so which are triggered on an event button click , but when I open those components all the other components are re-rendering .
export const AppFramework = (props) => {
const [isOpen, setIsOpen] = useState(false);
const [isSideNav, setIsSideNav] = useState(false);
const OptimizedRoutes = React.memo(AppRoutes);
useEffect(() => {
console.log('AppFramework Mounted');
}, []);
// Methods For Opening and closing of Login and SideNav
const handleOpen = useCallback(() => {
setIsOpen(true);
}, [isOpen]);
const handleClose = useCallback(() => {
setIsOpen(false);
}, [isOpen]);
const openSideNav = useCallback(() => {
return setIsSideNav(true);
}, [isSideNav]);
const LoginPopBody = (
<div className="pop-window">
<Button startIcon={<Close />} onClick={handleClose}></Button>
{/* {For Fields for loggin in .........} */}
</div>
);
return (
<Router>
<HideOnScroll>
<AppBar color="default" className="app-header">
<Button startIcon={<Menu />} onClick={openSideNav}></Button> // Button To Open Side Navbar
<Button onClick={handleOpen}>Login</Button> /* Button To Open Login Popup */
</AppBar>
</HideOnScroll>
//sidenav comp passing props to open and close below
<SideNav open={isSideNav} close={setIsSideNav} />
//routes are below
<main className="main-blk">
<Suspense fallback={<div>Loading...</div>}>
<OptimizedRoutes />
</Suspense>
</main>
<Modal open={handleOpen}>{LoginPopBody} </Modal>
</Router>
);
};
export default React.memo(AppFramework);
On opening and closing of both login popup and side navbar renders every component .I tried React.memo in each component but nothing happened , I hope hooks will give a solution for this.
And one more thing while entering each input on login popup form fields also renders every components behind the login popup . what will be the solution for these issues ?
This might not be the answer you are satisfied with but most of the time these problems pop up because of the way you design/construct your components. In react planning the components is the first and in my opinion the most important thing. It can save us from a lot of trouble. In this case You need to keep the components linked but independent of each other.
I would recommend you to read https://reactjs.org/docs/thinking-in-react.html. This might help you.
Changing the state will definitely re-render the whole component. But to avoid some components to stop re-rendering or re-render on come custom state changes you can use useCallBack hook.
The following article might help.
https://kentcdodds.com/blog/usememo-and-usecallback/
You can put the other component in individual functions and use useCallback hook on those function. This way you can define on which state change should those functions be used again.
Actually the issue is resolved , actually my problem is very simple , its not a complicated problem , To open the Side Navbar and Login Component I don't need to pass a boolean value from the parent component as a Prop .
Instead of that, we can get the boolean value from the own component by a passing a boolean value to the module to be shown in my case I used Material UI's Modal and Drawer by an event like button click or any other event .
This is what I did to solve my issue . In short I made the components independent.
in my web react project i have two class one "Menu" return the Menu for my dashboard (the links to change the pages "Home" , "messages" ....) and another one "Box" for show the pages (it's the container of my website pages ) so my question is how the page when i click button ?.
class menu(){
render(){
return(<button>click to go to home</button>) ;
}
}
class box(){
render (){
return(<Home>this is the home</Home>) ;
} }
You can create a parent component that has the page property and the setPage method.
Inside that component, render the toolbar and the pages. The toolbar should accept a prop that'll called onClickPage that should dispatch the setPage. For example:
const [page, setPage] = React.useState("dashboard");
....
<Toolbar onClickPage={page => setPage(page)} />
<Pages page={page} />
This is what's called Lifting the state up (https://reactjs.org/docs/lifting-state-up.html) I suggest you to read their docs. It's very beginners friendly.
If your main problem is to navigate between pages you can easily achieve this by using react-router.
https://reactrouter.com
I have a component which renders an image, when the user clicks on it I want it to render through a portal without having to reload the image which is what's currently happening, this is a simple example:
export const TestPortals = (props: any) => {
const element = <img src="imageurl.com/image.jpg" width={100} height={100} />
const [inPortal, setInPortal] = React.useState<boolean>(false)
return inPortal ? ReactDOM.createPortal(element, document.getElementById("portal")) : element
}
When the state switches to inportal = true the image itself reloads itself.
I'd like it to switch to rendering from the portal without that happening.
It is not possible, "portal" means to render React component outside of the root.
That means that when ever you changing the state to render a portal, React appends new Dom element to the body.
This means that the image got rendered by the browser second time, this is how browsers work, you can do anything in React to change this behaviour.
I have a component which has in its Class.propTypes a function onClick: onClick: PropTypes.func
In another component I'm using this component multiple times to populate a page. Each of these components have a title which when clicked should redirect to another page.
The problem I have is that it doesn't work when I click on it. It does nothing.
This is the render of the main component:
render() {
return (
<Class
title={account.AccountName}
onClick={() => "mySite/accountview?id=" + account.AccountName}
>
</Class>
...
);
}
What should I add to onClick to make it work?
You need use React Router.
With Link:
<Link to={`/mySite/accountview?id=${account.AccountName}`}>something</Link>
With onClick:
<button onClick={() => hashHistory.push(`/mySite/accountview?id=${account.AccountName}`)}></button>
You can use hashHistory or browserHistory :D