Load new component while adding href to url - javascript

I am trying to implement a system to when an item in the navbar is clicked it loads a new component on the screen. I am running into a problem making this a new URL and keeping my state. Let me clarify.
Here is my navbar code:
<Nav pullRight activeKey="1" onSelect={k => this.handleSelect(k)}>
<NavItem eventKey={1} href={'/component1'}>
Component 1
</NavItem>
<NavItem eventKey={2} href={'/component2'}>
Component 2
</NavItem>
</Nav>
Here is my handleSelect code:
handleSelect(eventKey, event) {
this.setState({
componentToLoad: eventKey
});
event.preventDefault();
}
In my render I do this:
render() {
if(this.state.componentToLoad === 'component1'){
return (<Component1/>);
} else if(this.props.componentToLoad === 'component2'){
return (<Component2/>);
}
The problem I'm running into is because of the href adding to the URL the page reloads and I lose my state so it always loads component 1 because I set it to that in the constructor. If I remove the href it works, but the URL is not how I want it.
How do I get this to load the component I want, add to the URL, and keep the state?
Thank you

As mentioned in the comments, React Router is a good choice for your requirements.
Here's a codesandbox using React Router, simply illustrating the functionality you describe.
You could also use the browser's History API (see pushState method) to manipulate the URL directly without a page reload, although be aware that there are some differences in the api in modern browsers.

Related

What exacly does NextJS Link component trigger and how to catch that event

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.

ReactJs applying javascript correctly

I have 3 pages reactjs application with a bootstrap template. I am using react-router to handle the page transitions.
So I have a index.js file containing something like:
function App() {
return(
<Router>
<Switch>
<Route path="/page1/">
<Page1 />
</Route>
<Route path="/page2">
<Page2 />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
);
}
In my index.html file, I defined some js functions to apply some effects to the pages, like a carousel and things like this. The weird thing is that when I reach page2 from clicking the link on page1 the js effects are applied, but when I refresh it the js effects are not applied...
this is my page2 file (I stripped all the unnecessary stuff for simplicity):
function Page2(){
useEffect(() => {
window.applyEffects();
})
return(
some jsx here...
)
}
am I doing something wrong here?
EDIT
I realised I forgot to add an important detail. I removed the useEffect and the call to window.applyEffects() in my component and the page renders as I would expect without the effect. If then I call window.applyEffects() from the developer console everything works fine. I suspect I should find a way to call the js function AFTER the page has been totally rendered. Is this doable?
SOLVED
I would like to thank everyone that replied and helped me in the right direction. I solved it by updating the useEffect hook like this:
useEffect(() => {
const script = document.createElement('script');
script.src = `${window.location.origin}/js/effects.js`;
script.async = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
}
});
I created a file called effects.js in the js folder, and I load it by appending it to the dom every time the component loads... also I remember to remove it with the return. I leave this here hoping it could help someone in my same situation in the future!
Thank you stackoverflow community !
I saw that you are using window object inside your code. Maybe that is the root of your problem. On the official documentation it says:
Window Object The window object represents an open window in a
browser.
If a document contain frames ( tags), the browser creates one
window object for the HTML document, and one additional window object
for each frame.
...which kind of suggests that it will fire once the window is open in the browser, hence I'm not sure that it will re-fire once you refresh the page. Having the useEffect hook should fire away anything when a page refreshes because it mounts the component again.
I suggest look into how you are using the window.applyEffects(). Here's a link to the official window documentation.
It seems that you just want to run a piece of code every time page is reloaded, checking it out I found it
React | How to detect Page Refresh (F5)
and it seems what you are looking for.

Send event to another class

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

'Move' a component into a portal?

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.

Render existing component (ref) as route

Problem
Our application injects other apps which are rendered using Iframes, the user can access these apps via a tab menu. Obviously the iframe src request can take some time, and it is not ideal to perform the request when the user clicks on the tab.
The app should appear as if it is part of our app - i.e. completely seamless and part of our bundle.
What I've Tried - Browser Caching
By simply making a call to the IFrame URL on app load, the browser will cache a majority of the required resources for each app.
To achieve this I created a simple component that renders an IFrame with the required app URL.
const PreloadIframe = props =>
(
<iframe
src={props.url}
></iframe>
);
I then render this component as display:none when the app loads. And when the user clicks on the required tab to load the real iframe, the bundle has already been cached and the transition is nearly seemless.
Problems with this solution
The client makes 2 requests to the app URL, once to cache and once again when it should actually be displayed
This cancels out any optimisation that the caching might provide, as the user must still wait on a response from the web service before they can use the injected app.
Ideally
I would like to set the <PreloadIframe/> component to visible when the user clicks the appropriate tab.
Is it possible to use
Route.render() or
Route.component
To render an existing component, i.e a component that is currently present in the DOM?
I ended up just creating a intermediary component IframeFactory
class IframeFactory extends Component {
componentDidMount() {
this.props.iframeRef.current.setVisible(true);
}
componentWillUnmount() {
this.props.iframeRef.current.setVisible(false);
}
render() {
return null;
}
This component takes a ref to an iframe.
Then in my <Route /> component I simply render this intermdiary component and pass in the appropriate ref.
{injectableApps ? injectableApps.map(app => (
<Route
key={`/${app.id}`}
path={`/${app.name}`}
render={() => (
<IframeFactory iframeRef={app.iframeRef} />
)}
/>
)) : null}

Categories

Resources