How reload page in react just once after componentDidMount - javascript

i have some problem which i can't figure out an hours..
Here is what:
i'm using react and react router.
I have a some component with own route like /about
and in this component i need to put some 3rd party javascript file which going with getting from some service some iframe to my page.
this service saying to me adding their script and rendering some div with specific id
i put this script to my component in componentDidMount method like that:
componentDidMount() {
var loadScript = function(src) {
var tag = document.createElement("script");
tag.async = false;
tag.src = src;
var body = document.getElementsByTagName("body")[0];
body.appendChild(tag);
};
loadScript("https://somewhere/js?for=myid");
}
and then in my render:
render() {
return (
<div className="content">
<div id="iframe_app"></div>
</div>
);
}
till here everything is normal but this script tag calling just once..
when i change route eg going another page of my app and back this component script not working not appearing in network tab.
But if i'm hard refreshing window with ctrl + r anything works again.
so i don't know why is happening and because of that i think to refresh page on componentDidMount directly with
window.location.reload()
but that time it's working in loop so my page refreshing infinity
how i can figure out with this?

if you really want this - add a unique parameter to the search string like loadScript("https://somewhere/js?for=myid&ver=" + Math.random()); // but you should use your own unique id generator instead of Math.random(), for example - this https://www.npmjs.com/package/uniqid

I suggest you to load the scripts in the constructor before the component is already loaded. It isn’t a best practice use setState from componentDidMount

Related

Router.push or Link not rendering/refreshing the page even thought the url is updated nextjs

I apologize for my horrendous way of explaining my issue. I have shared a link below description which is exactly the same issue I am experiencing. Any kind of help would be greatly appreciated.
I have directory path like pages/request/[reqid].js . When my url is localhost:3000/xyz and I navigate to pages/request/1 by clicking a button on the current page, the page successfully loads the page with proper data from [reqid=1] but when I try to access pages/request/[reqid].js with different reqid (say suppose reqid=2), the url reflects the correct the reqid pages/request/2 but the page remains the same, doesn't change. However if I go back to other pages like localhost:3000/xyz and click a button there to navigate to pages/request/2 it works but from within pages/request/[reqid] it doesn't render a page associated to the corresponding reqid even thought the url is updated. I have tried both Link and router.push ,both fails to render the correct reqid page.
https://github.com/vercel/next.js/issues/26270
It actually failed to include that I was using getServerProps to fetch the data, which was the reason the page wasn't rendering , unless the page was manually refreshed. The page state is not reset for navigation between dynamic routes that served by the same source component.
for example, give page source /a/[param]/index.js, when navigating from /test/123 to /test/124, states on the page wasn't being reset.
So actually happened is the same React Component been rendered with different props. Thus react takes it as a component is rerendering itself, and causing the new navigated page receive stale states.
To fix it, just add {key: } to page initial props or getserversideprops
export const getServerSideProps = async (ctx) => {
try {
const { reqid } = ctx.params;
//fetch code
return {
props: {
key: reqid,
data:data
},
};
} catch (error) {
console.log(error);
}
};

How to insert iframe created using js in a React component

I am trying to build an app with an iframe. It will have controllers to change the iframe style and add text to it. Iframe will be like a preview. I want to create the iframe with javascript. I tried this to create the element and append it to component return div like this:
let iframe = document.createElement('iframe');
document.querySelector('#iframeContainer').appendChild(iframe);
ı received this error :TypeError: Cannot read properties of null (reading 'appendChild')
I also tried another method :
let iframe = React.createElement('iframe', {});
ReactDOM.render(
iframe, document.getElementById('root')
);
in this method, because I don't have a return value, it throws an error. I am not able to insert it in a component.
How should I tackle this problem? I am open to all ideas.
I found a solution to this problem. While I was trying to append the element, I missed out on the component lifecycle. After spending good amount of time here is my solution :
const createIframe = () => {
const iframe = document.createElement('iframe');
document.getElementById('iframeContainer').appendChild(iframe);
}
useEffect(() => {
createIframe();
});
By calling my function in useEffect I made sure that div I wanted to append chil is created. I used useEffect but if you're using class component you should use componenDidMount()

Want to have an event handler for the browser's back button with next.js

I am having a modal which while opening pushes a hash to url example.com/#modal, on browser back button click, I want to recognise that event so that I can toggle the state of modal. the point is, since Im using it with next.js (server side rendering), I will not be having access to window object (correct me if I am wrong). so I need an alternate way to handle the event of browser back button.
You can use next/router's beforePopState to act on changes to the session history navigation (back/forward actions), and make sure it'll only happen when leaving the current page.
useEffect(() => {
router.beforePopState(({ as }) => {
if (as !== router.asPath) {
// Will run when leaving the current page; on back/forward actions
// Add your logic here, like toggling the modal state
}
return true;
});
return () => {
router.beforePopState(() => true);
};
}, [router]); // Add any state variables to dependencies array if needed.
#Summy I hope your issue is resolved by now. If not you can try this for the browser back button:-
Next.js + React Go back to the previous page
If you want to use hashbang URLs you can't use SSR, since /# and /#/one is the same route server-side, so there is no way for the server to know what to render, it will need to send a basic template and let the client fill it, in that case, I think using CRA or Parcel with React Router and its HashRouter is a better option, that way you will have a single index.html and let the client decide what to render.
in NextJs we can use beforePopState function and do what we want such close modal or show a modal or check the back address and decide what to do
https://stackoverflow.com/a/60702584/4717739
https://stackoverflow.com/a/69560739/4717739

What's the difference between hydrate() and render() in React 16?

I've read the documentation, but I didn't really understand the difference between hydrate() and render() in React 16.
I know hydrate() is used to combine SSR and client-side rendering.
Can someone explain what is hydrating and then what is the difference in ReactDOM?
From the ReactDOMServer docs (emphasis mine):
If you call ReactDOM.hydrate() on a node that already has this server-rendered markup, React will preserve it and only attach event handlers, allowing you to have a very performant first-load experience.
The text in bold is the main difference. render may change your node if there is a difference between the initial DOM and the current DOM. hydrate will only attach event handlers.
From the Github issue that introduced hydrate as a separate API:
If this is your initial DOM:
<div id="container">
<div class="spinner">Loading...</div>
</div>
and then call:
ReactDOM.render(
<div class="myapp">
<span>App</span>
</div>,
document.getElementById('container')
)
intending to do a client-side only render (not hydration).
Then you end with
<div id="container">
<div class="spinner">
<span>App</span>
</div>
</div>
Because we don't patch up the attributes.
Just FYI the reason they didn't patch the attributes is
... This would be really slow to hydrate in the normal hydration mode and slow down initial render into a non-SSR tree.
I don't have anything specific to add to what's been said above about the use of hydrate, but in trying to learn about it I put together a little example, so here's the work for whoever finds it helpful.
Goal
Serve two pages, one which uses ReactDOM.hydrate and one which uses ReactDOM.render. They will depend upon some react components written in JSX, which are loaded by <script> tags, given artificial delay (by the server) to illustrate the difference between hydrate and render.
Basic Structure
One file which has the HTML "skeleton"
One file with the custom React components written in JSX
One script which generates all pages for the server to use
One script to run the server
Results
After I generate the pages and run the server, I go to 127.0.0.1 and am presented with the header hydrate, a button, and two links. I can click the button, but nothing happens. After a few moments, the document finishes loading and the button starts counting my clicks. Then I click on the "render" link. Now, the page I'm presented with has the header render and two links, but no button. After a few moments, the button appears and is immediately responsive.
Explanation
On the "hydrate" page, all the markup is immediately rendered, because all the necessary html is served with the page. The button is unresponsive because there are no callbacks connected yet. Once components.js finishes loading, the load event fires from the window and the callbacks are connected with hydrate.
On the "render" page, the button markup isn't served with the page, but only injected by ReactDOM.render, so it isn't immediately visible. Note how the appearance of the page is jarringly changed by the script finally loading.
Source
Here is the custom react component I am using. It will be used by the server in node with react to statically render components, and it will also be loaded dynamically from the server for use in pages (this is the purpose of checking for exports and React objects at the beginning of the file).
// components.jsx
var exports = typeof(exports) == 'object' ? exports : {};
var React = typeof(React) == 'object' ? React : require('react');
function MyButton(props) {
[click, setClick] = React.useState(0);
function handleClick() { setClick(click + 1); }
return (
<button onClick={handleClick}>Clicked: {click}</button>
);
}
exports.MyButton = MyButton;
This is the script used to generate all the pages required for the server. First, babel is used to transpile components.jsx into javascript, then these components are used, along with React and ReactDOMServer, to create the actual pages. These pages are created with the fuction getPage which is exported from the file pageTemplate.js, shown next.
// genScript.js
let babel = require('#babel/core');
let fs = require('fs');
let ReactDOMServer = require('react-dom/server');
let React = require('react');
let pageTemplate = require('./pageTemplate.js');
script = babel.transformFileSync(
'components.jsx',
{presets : [['#babel/react']]}
);
fs.writeFileSync('components.js',script.code);
let components = require('./components.js');
hydrateHTML = pageTemplate.getPage(
'MyButton',
ReactDOMServer.renderToString(React.createElement(components.MyButton)),
'hydrate'
);
renderHTML = pageTemplate.getPage(
'MyButton',
'',
'render'
);
fs.writeFileSync('hydrate.html',hydrateHTML);
fs.writeFileSync('render.html',renderHTML);
This file just exports the getPage function mentioned previously.
// pageTemplate.js
exports.getPage = function(
reactElementTag,
reactElementString,
reactDOMMethod
) {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js" defer></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" defer></script>
<script src="./components.js" defer></script>
</head>
<body>
<h1>${ reactDOMMethod }</h1>
<div id="react-root">${ reactElementString }</div>
hydrate
render
</body>
<script>
window.addEventListener('load', (e) => {
ReactDOM.${ reactDOMMethod }(
React.createElement(${ reactElementTag }),
document.getElementById('react-root')
);
});
</script>
</html>
`;
}
Finally, the actual server
// server.js
let http = require('http');
let fs = require('fs');
let renderPage = fs.readFileSync('render.html');
let hydratePage = fs.readFileSync('hydrate.html');
let componentsSource = fs.readFileSync('components.js');
http.createServer((req, res) => {
if (req.url == '/components.js') {
// artificial delay
setTimeout(() => {
res.setHeader('Content-Type','text/javascript');
res.end(componentsSource);
}, 2000);
} else if (req.url == '/render.html') {
res.end(renderPage);
} else {
res.end(hydratePage);
}
}).listen(80,'127.0.0.1');
Hydrate is basically used in case of SSR(Server side Rendering). SSR gives you the skeleton or HTML markup which is being shipped from a server so that for the first time when your page loads it is not blank and search engine bots can index it for SEO(A use case of SSR). So hydrate adds the JS to your page or a node to which SSR is applied. So that your page responds to the events performed by the user.
Render is used for rendering the component on client side browser Plus if you try to replace the hydrate with render you will get a warning that render is deprecated and can't be used in case of SSR. it was removed because of it being slow as compared to hydrate.
In addition to above...
ReactDOM.hydrate() is same as render(), but it is used to hydrate(attach event listeners) a container whose HTML contents were rendered by ReactDOMServer. React will attempt to attach event listeners to the existing markup.
Using ReactDOM.render() to hydrate a server-rendered container is deprecated because of slowness and will be removed in React 17 so use hydrate() instead.
The entire process of putting functionality back into the HTML that was already rendered in server side React is called hydration.
So the process of re-rendering over the once rendered HTML is referred to as hydration.
So if we try to hydrate our application by calling ReactDOM.render() its supposed to be done by calling ReactDOM.hydrate().
render will flush out anything in the specified element(named as 'root' in most cases) and rebuild it ,while hydrate will keep anything that is already inside the specified element and build from that,making the initial page load faster.

React page does not rerender on url query change

I'm trying to create a react page having different content based on the query in the url. ex: things?type=0 and things?type=1 resulting in different filtered content.
The problem is, React isn't triggering a rerender of my page when switching to these pages. I need to manually refresh the page to get the desired result.
I tried to use the build in lifecycle features but non of them triggers the page to refresh. It tried to do my fetch in the componentWillReceiveProps method with then I have to click twice on the link to get my filtered content (which is better than no page refresh but not user friendly).
I use this reference.
You will probably want to use https://github.com/reactjs/react-router or some similar library for routes.
If you want to go on your own you have to listen to browser history changes. You could do it like this for example:
componentDidMount: function () {
var component = this;
window.onpopstate = function() {
console.log(window.history.state);
component.setState({
query: window.location.search
});
};
};
I am not sure if this is the good way to go, but it should work at least.

Categories