Custom Sanity Action Not Updating Variables When Switching Between Documents

2 replies
Last updated: Sep 30, 2022
I have a custom action built. It is a Publish button that is attached to the corresponding Netlify build hook. We have several microsites within our Sanity studio, and each microsite has its own Publish button that kicks off a deploy. In my action code I get the microsite id that then sets the necessary variables for the site deploy. However, when I bounce around to different microsites to test the publish button, it isn't resetting the variables to jive with the new microsite id.
A condensed version of my code:


import React, {useState} from "react";
import {RocketIcon} from '@sanity/icons'
let siteName;
let hookId;
let baseHookUrl = "<https://api.netlify.com/build_hooks/>";
let postUrl;

export function PublishMicrositeAction(props) {
    let micrositeId = props.id;

    if (micrositeId == "abc") {
        siteName = "Publishing Root Site";
        hookId = "123abc";
        postUrl = `${baseHookUrl}${hookId}`;
    }
    if (micrositeId == "def") {
        siteName = "Publishing Essentials";
        hookId = "456def";
        postUrl = `${baseHookUrl}${hookId}`;
    }
    if (micrositeId == "ghi") {
        siteName = "Publishing Foundations";
        hookId = "789ghi";
        postUrl = `${baseHookUrl}${hookId}`;
    }
    
    console.log(micrositeId)

    const [isDialogOpen, setIsDialogOpen] = useState(false)

    if (props.type !== 'microsite') {
        return null
    }

    function formSubmit() {
        var request = new XMLHttpRequest();
        request.open('POST', postUrl, true);
        request.onload = function() { // request successful
        // we can use server response to our request now
          console.log(request.responseText);
        };
      
        request.onerror = function() {
          // request failed
        };
      
        request.send(new FormData(event.target)); // create FormData from form that triggered event
        event.preventDefault();

        setIsDialogOpen(false)
    }
      
    return {
      label: "Publish Microsite",
      icon: RocketIcon,
      onHandle: () => setIsDialogOpen(true),    
      dialog: isDialogOpen && {
          type: 'modal',
          onClose:  () => setIsDialogOpen(false),
          content: <div id="modal"  style={{paddingBottom: 30 + 'px'}}>
          <h3>{siteName}</h3>
          <p>Please enter a brief description of what was changed.</p>
          <form id="publishForm" onSubmit={formSubmit}>
          <label for="trigger_title">Message: </label>
          <input type="text" id="trigger_title" name="trigger_title"/>
          <button id="submitBtn" type="submit" style={{marginLeft: 10 + 'px'}}>Publish</button>
          </form>
          </div>
      }
    }
  }
Maybe I have stared at this too long to see my mistake or what I am missing. But I really need the micrositeId to be updated every time I switch to a new microsite. I can see when I console.log the props.id that it displays the correct id, but the variables do not reflect that.
AI Update

I can see the issue in your code! The problem is that you're using module-level variables (siteName, hookId, postUrl) that are declared outside your component. These variables are set once when the module loads and persist across component re-renders, which is why they're not updating when you switch between microsites.

Here's what's happening:

  1. When your component first renders for microsite "abc", it sets the module-level variables
  2. When you navigate to microsite "def", React may reuse the same component instance or the module is already loaded, so those if statements run but the variables retain their old values or get overwritten in unexpected ways
  3. The closure in your formSubmit function captures whatever postUrl was at the time, not necessarily the current one

The fix: Move these variables inside your component function so they're recalculated on every render:

import React, {useState} from "react";
import {RocketIcon} from '@sanity/icons'

const baseHookUrl = "https://api.netlify.com/build_hooks/";

export function PublishMicrositeAction(props) {
    const [isDialogOpen, setIsDialogOpen] = useState(false)
    
    let micrositeId = props.id;
    let siteName;
    let hookId;
    let postUrl;

    // These now get set fresh on every render
    if (micrositeId == "abc") {
        siteName = "Publishing Root Site";
        hookId = "123abc";
    } else if (micrositeId == "def") {
        siteName = "Publishing Essentials";
        hookId = "456def";
    } else if (micrositeId == "ghi") {
        siteName = "Publishing Foundations";
        hookId = "789ghi";
    }
    
    postUrl = `${baseHookUrl}${hookId}`;
    
    console.log(micrositeId, hookId, postUrl) // Better debugging

    if (props.type !== 'microsite') {
        return null
    }

    function formSubmit(event) { // Don't forget to accept event parameter!
        event.preventDefault();
        
        var request = new XMLHttpRequest();
        request.open('POST', postUrl, true);
        request.onload = function() {
          console.log(request.responseText);
        };
      
        request.onerror = function() {
          console.error('Request failed');
        };
      
        request.send(new FormData(event.target));
        setIsDialogOpen(false)
    }
      
    return {
      label: "Publish Microsite",
      icon: RocketIcon,
      onHandle: () => setIsDialogOpen(true),    
      dialog: isDialogOpen && {
          type: 'modal',
          onClose:  () => setIsDialogOpen(false),
          content: <div id="modal" style={{paddingBottom: 30 + 'px'}}>
          <h3>{siteName}</h3>
          <p>Please enter a brief description of what was changed.</p>
          <form id="publishForm" onSubmit={formSubmit}>
          <label htmlFor="trigger_title">Message: </label>
          <input type="text" id="trigger_title" name="trigger_title"/>
          <button id="submitBtn" type="submit" style={{marginLeft: 10 + 'px'}}>Publish</button>
          </form>
          </div>
      }
    }
}

Key changes:

  1. Moved siteName, hookId, and postUrl inside the component function
  2. Changed if statements to else if for better logic flow
  3. Added event parameter to formSubmit (you were using it but not declaring it)
  4. Changed for to htmlFor in the label (React JSX syntax)

Even better approach - use a lookup object for cleaner code:

const MICROSITE_CONFIG = {
  abc: { name: "Publishing Root Site", hookId: "123abc" },
  def: { name: "Publishing Essentials", hookId: "456def" },
  ghi: { name: "Publishing Foundations", hookId: "789ghi" }
};

export function PublishMicrositeAction(props) {
    const [isDialogOpen, setIsDialogOpen] = useState(false)
    
    const config = MICROSITE_CONFIG[props.id];
    const postUrl = `${baseHookUrl}${config?.hookId}`;
    
    // ... rest of your code using config.name and postUrl
}

This makes it much easier to maintain and avoids the if/else chain entirely!

Show original thread
2 replies
I wonder if you're having some component remounting/rerendering sorcery interfering? Have you tried holding the variables in state?
FYI - I ended up changing the code to use a switch statement instead of an if statement. It is doing the job I had originally intended. Phew!

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?