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.
Related
I'm a React noobie.
I'm working on my current project which I use HTML video Element.
I'd like to add chapters to Video by using React state, but when I try, Video Element refreshes and playback location moves to the beginning.
How Can I prevent refreshing Video HTML on React State Update?
Try creating a separate video component and pass URL as a prop. Wrap that in React.memo. Now that component will re-render only when the URL changes.
const Parent = () => (<div><VideoComponent url={"SOME_URL"}/></div>)
const VideoComponent = React.memo(function MyVideoComponent({url}) {
// only renders if url have changed!
return (<video src={url}></video>)
});
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 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 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>)
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}