Using script in reactive environment

Hi there,

Thanks for creating Memberstack, it looks very promising ! I’m trying to integrate a registration/free membership and gated sections in a Gatsby (React) site and got some trouble : memberstack works well on site load, but stop working on pages changes. Here’s the buttons I use:

  <button ms-profile="true">profile</button>
  <button div data-membership-id="MY_MS_ID" ms-hide-element="true">login</button>

It seems the script load well: I can access to window.memberstack, window.$memberstack and window.selectMembership (?) in any page. However the buttons only works at first page mount. Because of Gatsby architecture, they are un-mounted/re-mounted at each page changes.

My guess is that memberstack initialize buttons and tags after the script finish loading (MemberStack.onReady promise) but do not re-try binding actions when the DOM updates. It would be super-helpful to be able to destroy the script and re-init it when needed:

export const AuthButton = () => {
  useEffect(() => {
    window.memberstack.init(); // mount
    return () => {
      window.memberstack.destroy(); // unmout
    }
  }, [])
  return <>
    <button ms-profile="true">profile</button>
    <button div data-membership-id="MY_MS_ID" ms-hide-element="true">login</button>
  </>;
};

That would make memberstack super easy to use with modern frameworks ! Any tips for using memberstack in this context would be much appreciated :slight_smile:

@aziaziazi thanks for posting!

We definitely need to add a way to reinitialize the memberstack script. I am going to add this to my list to checkout this week. I’ll reach out once I have a dev environment setup to test possible solutions :+1:

For now though, I believe there is a way to get MemberStack to work on Gatsby sites. We had another user of ours solve it by using normal a tags for links instead of gatsby link tags. Not sure if that’s an option for you or not…

Thanks for replying so quickly @belltyler !

Yeah I found out <Link /> or any other react component won’t work and we should use native tags instead. For react app, the deeper problem is that DOM elements are often mounted/unmounted, while memberstack assume everything’s here when script’s ready. A way to handle this this would be to clean of the script (maybe with window.memberstack = undefined) then loading the script again. This won’t be performant, but I guess could work:

window.memberstack = undefined;

// somehow remove script to document

let script = document.createElement('script');
script.src = 'https://api.memberstack.io/static/memberstack.js?custom';
script.setAttribute('data-memberstack-id', MEMBERSTACK_ID);
script.async = true;
document.body.appendChild(script);

I managed it instead by placing on top of the tree the components containing memberstack elements, that way they never re-mount. It’s a bit harder to position and need to be hidden/shown with css, but it only require memberstack to load once. I did it in wrapRootElement within gatsby-browser/ssr.js and it can be done in any react app placing the component on top of the tree.

The real challenge was to hide nested elements or pages without ms-hide="hidden-group-name". For that, I use a top DOM provider component to:

  • load memberstack script and get the member data
  • storing the state of current private page visit
  • passing everything by context to the tree, so deeper nested article components can access the member object and update private page

I’m good for now with my setup but great you’re working on extending the api :ok_hand: . It will be useful !