-
Notifications
You must be signed in to change notification settings - Fork 660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How do I know the scripts are ready? #146
Comments
if you're server side rendering helmet then the scripts loaded in |
script We ended up just checking global ( Even if you use server-side rendering it may be useful since sometimes you only want to load script after some user action on client (for example - loading Facebook SDK only if it specifically requested). Save you precious |
I am in need of an onLoad callback too =) @romanonthego the native DOM What does your setInterval implementation look like? Thanks |
Is there any plans to add a |
export default function waitForGlobal(name, timeout = 300) {
return new Promise((resolve, reject) => {
let waited = 0
function wait(interval) {
setTimeout(() => {
waited += interval
// some logic to check if script is loaded
// usually it something global in window object
if (window[name] !== undefined) {
return resolve()
}
if (waited >= timeout * 1000) {
return reject({ message: 'Timeout' })
}
wait(interval * 2)
}, interval)
}
wait(30)
})
} |
FYI we went with the following solution to hack in the onload functionality /cc @romanonthego :
|
@navgarcha Thanks, definitely much better approach than timers :) Works like a charm although I have rather used |
Good call - save myself from the 'no-param-reassign' eslint warning too ;) |
In the end I've just used
It doesn't work on a server thou ... I guess you have to stick to helmet in that case. |
I am running into this issue as well. Has anyone taken a stab at another solution? I like the power of react-helmet for SSR, but would love it even more if we can wait for helmet to finish making updates to the DOM before rendering/executing/doing work. Scenario is to async download and evaluate lodash library and use it in the component before mounting or updating. Potentially have Helmet used with a higher order component to delay mounting until Helmet scripts are on the DOM. |
Somehow I was sure this functionality was already there. If it allows us to add |
Strangely, I could not get this to work—and it seems unnecessarily difficult to work around. class Shell extends BaseComponent {
// …
handleResourceInjected({ resourcesByType }) {
// each's might be slightly different (I don't remember the exact code)
// the take-away is it looped over each resource-type group (styles, scripts, etc)
// and then into each resource to check for an `onload` and call it if defined
_.each(resourcesByType, (resourceType) => _.each(resourceType, ({ onload }) => {
if (typeof onload === 'function') {
debugger; // pauses
onload(); // is indeed defined (shows bound function) &
// step-into jumps to the end of the callback,
// skipping debugger
}
}));
}
// …
render() {
return (
// …
<Helmet
onChangeClientState={
(newState, resources) => this.handleResourceInjected(resources)
}
/>
// …
);
}
}
class SomeComponentWithUniqueDep extends BaseComponent {
handleDepLoaded() {
debugger; // never hit
// do stuff
}
render() {
return (<React.Fragment>
<Helmet>
<script
async
onLoad={() => this.handleDepLoaded()}
src="…"
/>
</Helmet>
// …
</React.Fragment>);
}
} I also tried Note that "BaseComponent":
|
#299 This PR seems to address this issue, but has not been merged yet :( |
I ran into a similar need today and implemented something like this: Basically, make the external library reference a property of the state for the component. Make whatever component depends on that external library wait until |
This does the trick for me:
This works pretty well with scripts like google analytics, stripe & co as they create an accessor in the global scope. |
Using |
Yes - ignore.
Le lun. 22 juil. 2019 à 23:35, Pierre-Eric Marchandet <
[email protected]> a écrit :
… This does the trick for me:
const myScriptUrl = 'https:/blablabla/'
const MyComp = () => {
const [scriptLoaded, setScriptLoaded] = useState(typeof window !== 'undefined' && typeof myScript !== 'undefined')
const handleChangeClientState = (newState, addedTags) => {
if (addedTags && addedTags.scriptTags) {
const foundScript = addedTags.scriptTags.find(({ src }) => src === myScriptUrl)
if (foundScript) {
foundScript.addEventListener('load', () => setScriptLoaded(true), { once: true })
}
}
}
return <>
<Helmet onChangeClientState={handleChangeClientState}>
{typeof window !== 'undefined' && typeof myScript === 'undefined'
&& <script async defer src={stripeUrl} />}
</Helmet>
{scriptLoaded && ...}
</>
}
This works pretty well with scripts like google analytics, stripe & co as
they create an accessor in the global scope.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#146?email_source=notifications&email_token=AKNGXKHDT63TKRR5N3IDKDTQAWZQXA5CNFSM4CEQGIGKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2P54XA#issuecomment-513793628>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AKNGXKFABVJVRXS2F5CRVPTQAWZQXANCNFSM4CEQGIGA>
.
|
I used the useScript hook on https://usehooks.com/useScript/ and worked pretty good as an alternative solution to helmet :) |
Note that |
My solution is like this:
Notes:
|
Seems Helmet has transform the onLoad function to inline script. function scriptOnload(el: HTMLScriptElement){
const {id, foo} = el.dataset;
// script initialize with data id & foo
}
<script
src={url}
async
data-id={id}
data-foo={foo}
// @ts-ignore - Helmet will transform the onload function to inline script
onLoad={`(${scriptOnload.toString()})(this)`}
/> |
An approach that seems to work well client side is: const HelmetScript = ({ src, onLoad }: { src: string; onLoad: () => void }) => {
const uid = useUuid();
const onChangeClientState = useCallback(() => {
const scriptElem = document.getElementById(uid);
if (scriptElem !== null) {
scriptElem.onload = onLoad;
}
}, [onLoad, uid]);
return (
<Helmet onChangeClientState={onChangeClientState}>
<script id={uid} src={src} async={true} />
</Helmet>
);
}; |
I use Helmet to load scripts like firebase library. Is there an event handler that tells me when the scripts are loaded and ready? Now I can't access my library since I use it inside
componentDidMount
but the library is downloaded asynchronously afterrender
HelmetThe text was updated successfully, but these errors were encountered: