✨Discover storytelling in the AI age with Pixar's Matthew Luhn at Sanity Connect, May 8th—register now

Trouble passing data from other fields into a custom component in Studio.

12 replies
Last updated: Aug 23, 2023
I’ve got a custom component loaded into Studio. It belongs to a post schema. I’m have trouble figuring out how to pass data from the other fields into it. For example I might want to utilize the values of title, except, and body, in it. It’s essentially a hidden form from which I’m going to build a json structure to fire off to the outside world.
Aug 19, 2023, 2:10 AM
There is a helper function called
useFormValue()

import {useFormValue} from 'sanity'
const form = useFormValue([])
Aug 19, 2023, 2:13 AM
omg I should have asked you three hours ago lol. Thank you! haha I was exploring all the wrong rabbit holes.
Aug 19, 2023, 2:17 AM
I really appreciate it.
Aug 19, 2023, 2:17 AM
Only three hours? That's not bad. I've been spending 300 hours with
preview-kit
😉
Aug 19, 2023, 2:17 AM
haha
Aug 19, 2023, 2:17 AM
I’m having a hell of a time passing values from useFormValue to an api route. I have a component that is hitting the endpoint and firing off the email but I can’t seem to get any of the values through. I have undefined values across the board it seems, except when logging the values I do in fact seem them in the console.
Aug 23, 2023, 12:09 AM
I’m having a hell of a time passing values from useFormValue to an api route. I have a component that I am loading into my post editor that is hitting the endpoint and firing off the email but I can’t seem to get any of the values through. I have undefined values across the board it seems, except when logging the values I do in fact seem them in the console. They are there, but they are not there. I can print them out in the component for example, but they are not really there. 😞


// lib/sanity/schemas/SendEmail.jsx
import { toHTML } from '@portabletext/to-html';
import React from 'react';
import { useFormValue } from 'sanity';

const components = {
  types: {
    image: ({ value }) => {
      if (value && value.asset && value.asset._ref) {
        return `<img src="${value.asset._ref}" />`;
      } else {
        return '';
      }
    },
    embed: ({ value }) => {
      if (value && value.url) {
        return `<iframe src="${value.url}" />`;
      } else {
        return '';
      }
    }
  }
};

export default function SendEmail(props) {
  const form = useFormValue([]);
  const [title] = useFormValue(['title']);
  const [body] = useFormValue(['body']);
  const [section] = useFormValue(['section']);
  const [subTitle] = useFormValue(['excerpt']);
  const bodyHtml = toHTML(body, { components });

  const authorName = 'Author Name';
  const authorSlug = 'author-slug';
  const authorImageUrl = 'author-image';

  const handleSendEmail = async () => {
    // Prepare the request body
    const requestBody = {
      title: title[0],
      subTitle: subTitle[0],
      section: section[0]
    };

    try {
      const response = await fetch('/api/send-email', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(requestBody)
      });

      if (response.ok) {
        setEmailSent(true);
      } else {
        console.error('Email sending failed');
      }
    } catch (error) {
      console.error('An error occurred:', error);
    }
  };

  return (
    <div className="space-y-2 border p-4">
      <h2 className="text-xl font-bold">Send Email</h2>
      <div className="flex flex-col space-y-2">
        <button onClick={handleSendEmail}>Send Email</button>
      </div>
    </div>
  );
}
and then in the route.ts


// app/api/send-email/route.ts

import { render } from '@react-email/render';
import NewsletterEmail, { NewsletterEmailProps } from 'emails/newsletter';
import { NextApiRequest, NextApiResponse } from 'next';
import { Client } from 'postmark';

const postmarkClient = new Client(process.env.POSTMARK_API_KEY || '');

export async function sendEmailHandler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method === 'POST') {
    try {
      const { title, subTitle, section } = req.body as NewsletterEmailProps;

      console.log('Received request to send email:', {
        title,
        subTitle,
        section
      });

      const emailHtml = render(
        NewsletterEmail({
          title,
          subTitle,
          section
        })
      );

      console.log('Generated email HTML:', emailHtml);

      const response = await postmarkClient.sendEmail({
        From: '<mailto:newsletter@redacted-domain.us|newsletter@redacted-domain.us>',
        To: '<mailto:reader@redacted-domain.us|reader@redacted-domain.us>',
        Subject: 'Newsletter',
        HtmlBody: emailHtml
      });

      console.log('Email sent successfully:', response);

      if (response.ErrorCode) {
        console.error('Error sending email:', response.Message);
        return res.status(500).json({ error: 'Failed to send email' });
      }

      return res.status(200).json({ message: 'Email sent!' });
    } catch (error) {
      console.error('Error sending email:', error);
      return res.status(500).json({ error: 'Failed to send email' });
    }
  } else {
    return res.status(405).json({ error: 'Method not allowed' });
  }
}

export { sendEmailHandler as POST };
Aug 23, 2023, 12:12 AM
When you're referring to things being undefined but logging, is my interpretation correct that everything works including the logs in the API route but just the email itself, as-delivered, doesn't contain anything where the values are expected to show?
Aug 23, 2023, 12:59 PM
Actually the logs in the API route are showing undefined. I have not hooked up those values to the email template. I just have hard-coded the bare minimum as needed in the route to both send via postmarkapp api as well as draw from the email template - these two things work great. So, I’m attempting to get everything declared correctly from the sendEmail component and then logging on the API route to check that I am indeed able to capture the values from the useFormValue.
Been toying with useMemo and useState to no avail. Here is the present sendEmail component:


// lib/sanity/schemas/SendEmail.jsx
import { toHTML } from '@portabletext/to-html';
import React from 'react';
import { useMemo } from 'react';
import { useFormValue } from 'sanity';


const components = {
  types: {
    image: ({ value }) => {
      if (value && value.asset && value.asset._ref) {
        return `<img src="${value.asset._ref}" />`;
      } else {
        return '';
      }
    },
    embed: ({ value }) => {
      if (value && value.url) {
        return `<iframe src="${value.url}" />`;
      } else {
        return '';
      }
    }
  }
};

export default function SendEmail(props) {
  const form = useFormValue([]);
  const title = useFormValue(['title']);
  const section = useFormValue(['section']);
  const subTitle = useFormValue(['excerpt']);
  const preface = useFormValue(['preface']);
  const body = useFormValue(['body']);
  const bodyHtml = toHTML(body, { components });

  // const authorName = 'Author Name';
  // const authorSlug = 'author-slug';
  // const authorImageUrl = 'author-image';

  console.log('form', form);
  console.log('title', title);
  console.log('section', section);
  console.log('subTitle', subTitle);
  console.log('preface', preface);
  console.log('bodyHtml', bodyHtml);

  const [formValues, setFormValues] = React.useState({
    title: '',
    section: '',
    subTitle: '',
    preface: '',
    bodyHtml: ''
  });

  React.useEffect(() => {
    setFormValues({
      title: title,
      section: section,
      subTitle: subTitle,
      preface: preface,
      bodyHtml: bodyHtml
    });
  }, [title, section, subTitle, preface, bodyHtml]);

  const handleSendEmail = async () => {
    // Prepare the request body
    // I only want to send the title, section, and subTitle right now
    const requestBody = {
      title: formValues.title,
      subTitle: formValues.subTitle,
      section: formValues.section
    };

    console.log('Request body:', requestBody);

    try {
      const response = await fetch('/api/send-email', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(requestBody)
      });

      if (response.ok) {
        setEmailSent(true);
      } else {
        console.error('Email sending failed');
      }
    } catch (error) {
      console.error('An error occurred:', error);
    }
  };

  return (
    <div className="space-y-2 border p-4">
      <h2 className="text-xl font-bold">Send Email</h2>
      <div className="flex flex-col space-y-2">
        <pre>{title}</pre>
        <button onClick={handleSendEmail}>Send Email</button>
      </div>
    </div>
  );
}
Aug 23, 2023, 4:57 PM
I apologize if I sound obtuse but at what point before sending can you no longer see the values? You mentioned they show in the component so I'm assuming they're able to be pulled from the hook.
Does it get as far as them for sure being in the requestBody on the front end before being sent?
Aug 23, 2023, 5:13 PM
Not obtuse at all. I made a quick screen-share and posted the offending files in a commit. Not sure how else to describe the situation.vid:
https://www.loom.com/share/f2a33f455fec405b85c9a04250e6007e?sid=091352b7-7502-4c86-833e-0422e578fcfd gh:
https://github.com/Pirate-Wires/pirate-wires/commit/c09c58d3e6a1d1ef54de4605534af3d3a9c55f08
Aug 23, 2023, 5:38 PM
And yes they appear to be in the requestBody on the front end. Perhaps not being handled correctly when sending or being received correctly. It may be more of a API issue at this point as the useFormValue work in the context of Sanity Studio appears to be good.
Aug 23, 2023, 5:51 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?