react-async-script icon indicating copy to clipboard operation
react-async-script copied to clipboard

How do you pass params to the URL?

Open darewreck54 opened this issue 5 years ago • 7 comments

Hi,

In the example shown, the URL is known upfront and hardcoded. Is it possible to pass in props into the wrapper that helps determine what the URL is and then inject that URL being used?

In my use case, bing maps api, I need to add in query params to set the Language or local.

example of what i'm looking for.

<WrapperComponent urlProps={} {… componentProps} />

Then in the WrapperComponent it takes the url props and builds the URL that is injected into the default.

export default AsyncScriptLoader(BingMapWidget, URL, { callbackName: callbackName, globalName: globalName, });

However, its not clear to me if it's possible to expose the wrapper to take in those params so then i can run a method to compute the URL and spit it back to the AsyncScriptLoader

Also i notice that you are attaching to the global scope. What happens if you launch two instances of the wrapped components. Would there be issues since they will share the same callback?

Thanks, Derek

darewreck54 avatar Aug 10 '18 17:08 darewreck54

Hi!

Also i notice that you are attaching to the global scope. What happens if you launch two instances of the wrapped components. Would there be issues since they will share the same callback?

So if they have the same url they will attach to the same script and everything is fine. It attaches to global scope because most of the libraries that load asynchronous attach something in the global scope, so it has to pass it through properly.

If they have different urls and they both attach to the same object, I don't see how that could work.

Also i notice that you are attaching to the global scope. What happens if you launch two instances of the wrapped components. Would there be issues since they will share the same callback?

In my use case, bing maps api, I need to add in query params to set the Language or local.

The latest version support a function as the getScriptUrl parameter. Which would allow you to change thing at run time. The getUrl function is called on componentDidMount

dozoisch avatar Aug 10 '18 22:08 dozoisch

Thanks for the response.

I notice that there are the two examples load up the values differently.

export default makeAsyncScriptLoader(URL, {
  callbackName: callbackName,
  globalName: globalName,
})(ReCAPTCHA);

While the other one on NPM https://www.npmjs.com/package/react-async-script

export default makeAsyncScriptLoader(ReCAPTCHA, URL, {
  callbackName: callbackName,
  globalName: globalName,
});

When I step through the library and use the first example, I notice that I get an exception thrown

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

Is the first method valid?

  1. Any suggestion on how to debug why the call back is not being made. This is the script tag that i'm trying to load. It's suppose to trigger onMapLoaded. However, it never hits that break point.

https://www.bing.com/api/maps/mapcontrol?setMkt=en-us&setLang=en-us&callback=onMapLoaded

url based off code example: https://github.com/Microsoft/BingMapsV8CodeSamples/blob/master/Samples/Map/Localize%20the%20Map%20(en-GB).html

In the example you have, you associate the callback in two locations. One in the Url and the other as parameter into the library. What does the parameter in the library do and where does the callback need to live? Is it in the wrapper?

Thanks!

darewreck54 avatar Aug 10 '18 22:08 darewreck54

@darewreck54 so first method, is the next API. It's not published yet! It's part of the upcoming 1.0 release, should've probably made a 1.0 branch instead of merging to master. Here is the documentation that works with the current version: https://github.com/dozoisch/react-async-script/blob/d86c42cdefe49a71ae71d52109dcab143217ebe4/README.md

Sorry about this confusion!

About the callback:

The callback name in the url is used by the library you are loading, in this case bing maps. The callback name passed to react-async-script is used to register a global callback so that it can receive the load event from Bing. So the names need to match :)

dozoisch avatar Aug 10 '18 23:08 dozoisch

The first thing i did was validate the the https://www.bing.com/api/maps/mapcontrol?setMkt=en-us&setLang=en-us&callback=onMapLoaded was valid in loading and making the callback. I achieve this by copying that example on in https://github.com/Microsoft/BingMapsV8CodeSamples/blob/master/Samples/Map/Localize%20the%20Map%20(en-GB).html an debug the code to make sure the callback is made.

So I know for sure the url is valid. However in my use case, I'm not seeing the call back triggered at all. The code is pretty straight forward:

import AsyncScriptLoader from 'react-async-script';
class BingMapWrapper extends React.Component {
  constructor(props) {
        super(props);
    }

    private onLoad(): void {
        console.log("script loaded");
    }

    public onMapLoaded(): void {
        console.log("Map onMapLoaded loaded");
    }

    public render(): any {
        const callbackName: string = 'onMapLoaded';
        conse url: string = 'https://www.bing.com/api/maps/mapcontrol?setMkt=en-us&setLang=en-us&callback=onMapLoaded'
        const Component: any = AsyncScriptLoader(BingMap, url, {
            callbackName: callbackName,
            globalName: "tempName",
            exposeFuncs: ['method1', 'method2'],
        });

        return <Component  asyncScriptOnLoad={this.onLoad} {...this.props}/>;
    }
} 

class BingMap extends React.Component {
        public onMapLoaded() {
                  console.log("bingMapWrapper called");
        }
} 

In this example, I'm not see how the class method is mapped to the global part. Am i doing it wrong? I would expect either one of the onMapLoaded to be triggered?

In the example, I don't really see the callback implemented. Is there a specific way it should be done?

The onLoad() does get triggered thou. Also the reason why i have it like this is because the BingMapWrapper takes in a para via prop to inject into the Url. I just didn't show it in this example.

Thanks, Derek

darewreck54 avatar Aug 10 '18 23:08 darewreck54

react-async-script puts the callbackName on window for you, window[callbackName] and assigns an internal callback to trigger after it has loaded. here

After the script loads an internal setState is called to trigger a re-render. so your <BingMap /> component will be called with props <BingMap tempName={window['tempName']} />, code

So you can either check in BingMap if props.tempName !== undefined then do map stuff or do something when asyncScriptOnLoad fires.

  • the callbackName string option needs to match the callback name in the url query. &callback=onMapLoaded that is all.

  • the only callback fired after the script has loaded that the user currently has access to is the asyncScriptOnLoad which looks like you are using above. is that one firing correctly?

I too apologize for the confusion in the readme on master. We are working towards a v1.0.0 release and @dozoisch is correct a branch would have been best.

hartzis avatar Aug 10 '18 23:08 hartzis

@hartzis AsyncScriptOnLoad does get called and I see the setState trigger. However, when I look at the code

window[globalName] is always undefined. When I actually look at the source code, I never see it set.

I put a break point in both the BinMap's constructor and componentWillReceiveProps(), and the props.[globalName] is always undefined even when the onload method gets called.

darewreck54 avatar Aug 11 '18 01:08 darewreck54

the globalName needs to be the name of the global that the script loads to.

for google recaptcha it is always window.grecaptcha. doc

for bing maps it appears to be Microsoft so it is accessible via window.Microsoft.

Which means you want to use globalName = 'Microsoft' and your component will get passed a props.Microsoft object when the bing maps script loads. microsoft bing ref docs

I got it working in this example codesandbox, please give it a try and let us know. Example code sandbox: https://codesandbox.io/s/nwwo3wlyo0

hartzis avatar Aug 11 '18 05:08 hartzis