I use Next.js and Material UI. I need to render an element in another place, because of the Swiper library that I use.
For this purpose, I'm using Material UI's <Portal> component and here's my code:
import { useRef } from 'react'
import Portal from '#mui/material/Portal';
//more code here
const container = useRef(null);
//more code here
<div ref={container}></div>
//more code here
<Swiper
spaceBetween={20}
slidesPerView={1}
>
<Portal container={container.current}>
<SwiperButton></SwiperButton>
</Portal>
{
ads.map(ad => <SwiperSlide key={ad.imageUrl}>
<div>{ad.title}</div>
</SwiperSlide>
)}
</Swiper>
This code works just fine for the first load of my web page. But as soon as I refresh the web page, it vanishes and disappears completely and there is no error whatsoever in the console.
And no matter what I do, it won't come back. Ctrl+F5 does not work and stopping and destroying and recreating my container does not work.
The only way to make it work is to comment that part of code, load the page and uncomment it again.
Why is it so, and how can I fix this problem?
Related
I'm relatively somewhat new to the whole world of react, DOM, etc.
This probably sounds like a simple issue or a dumb question, bear with me.
My code for the routing stuff looks like:
function App() {
return (
<div className="content">
<SideBar>
<Router>
<Routes>
<Route path="/dashboard" element={Dashboard} />
</Routes>
</Router>
</SideBar>
</div>
);
}
export default App;
My SideBar is a component of a sidebar, it should allow users to click on a button and to navigate to a new page. In this case, a dashboard. However, all pages basically load at the start of me going to my localhost/
I've tested this out due to a console.log loading from the dashboard basically popping up in other places, not just localhost/dashboard.
I'm using MaterialUI, React.
My SideBar.js looks like this:
<List>
{BarData.map((item, key) => {
return (
<ListItemButton
key={key}
onClick={() => {
window.location.pathname = item.link;
}}
Has things removed, but here is my SideBar data (in another folder).
export const BarData = [
{
text: "Dashboard",
icon: <HomeRounded color="#637381" />,
link: "/dashboad",
},
Am I doing something wrong here? I'm completely new to React, DOM, etc. So I'm unsure if this is the best way to go in general.
In short, what I'm trying to achieve is:
Have multiple pages, if a user clicks a button, it'll navigate them to that specific page, in this case a Dashboard.
Not have every page load at once(?) I'm still unsure on why this is, is it due to me importing the dashboard page and the element is grabbing it, not working fully, so it's just loading what the page shows?
In my react app, I have a header with a show dialog button.
This header component is included in all pages as necessary.
My current logic to show the dialog is as follows:
When user clicks the button, I dispatch an action to redux store { type:SHOW_DIALOG }, and let the parent component handle the state change and draw the dialog. I am using material-UI swipeable drawer component.
Handle Click event in an iconbutton in header component...
const handleClick = () => {
/*
* temporary bypass surgery to avoid going through login dialog
* dispatch({type:'SHOW_CONTACT_INFO'})
* return;
*/
if (!isLoggedIn) dispatch({type: 'SHOW_LOGIN_DLG'})
else router.push('/my-account')
}
In the parent page, where the header is contained...
// ...other code
<Container>
{/* Whether or not to show the login dialog . */}
{loginProcessState>=1 && loginProcessState<=7
?<LoginDialog type={loginProcessState===1?"login":"otp"} />
: null}
{/* if contact edit dialog is set to show, show it. */}
{contactEditDisplay? <ProfileEditDialog />:null}
</Container>
);
But, since I have many pages (around 10 pages and server side rendering with NextJS), I will have to repeat the state management in all those pages where the dialog must be shown.
Excuse my lack of knowledge here. Is it possible to avoid this dialog state check in the parent component / page. If so, how can i do it?
Suggestion:
Move the LoginDialog component up and render it directly within the root <App/> component.
And then use React.useContext to call that dispatch in various components to trigger loginProcessState.
The above should help you render the Login acros all components.
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.
I am using the "Toolbar" and "Copy To Clipboard" plugins with PrismJS to display a code snippet and a toolbar button to copy the code to clipboard and am loading my Prism js and css files in my <head>.
PrismJS works as expected if I start directly to the page (ex: http://localhost:3000/demo/example).
But if I navigate away from the page and return to it or start from the home page (http://localhost:3000/) and navigate to the page with the code snippet the button does not show up.
When I inspect the code I can see that the <div class="toolbar"> that normally holds the Copy button is missing.
I tried loading the PrismJS file with plugins via a plugin and adding the file in nuxt.config.js and that prevents the Copy button showing up at all.
Why might this be happening?
After checking out this blog I realized I needed to "highlight" all the prism codeblocks in the Mounted lifecycle like so:
...
import Prism from "~/plugins/prism";
export default {
mounted() {
Prism.highlightAll();
},
...
I didn't use the prism npm package like in the article, I just downloaded it from the PrismJS homepage and included it and the styles in my nuxt.config.js.
For React users, you can do that using react useEffect hook, something like this:
useEffect(() => {
const highlight = () => {
Prism.highlightAll();
};
highlight();
}, []);
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}