🔮 Sanity Create is here. Writing is reinvented. Try now, no developer setup

Help with updating a field in a Sanity document using customComponent and useDocumentOperation

24 replies
Last updated: Apr 24, 2023
Hi everyone, I need a help with customComponent I am trying to change another field from the same document by
const { patch } = useDocumentOperation(props.id, props.schemaType.name);
patch.execute([{ set: { path: pathSegments, value: 'testValue' } }])
but the value of the target field is not changing, What am I missing?
Apr 23, 2023, 8:49 PM
similar question to mine! I've just done some digging, and found
useFormValue
in the docs (https://www.sanity.io/docs/studio-react-hooks#5cb6622b6381 )
you can use this as such to get the document ID:

const documentId = useFormValue(["_id"]);
Now, if the document is a draft, it will be prefixed with
draft.
So that needs to be removed in order to pass it into
useDocumentOperation

e.g:

const documentId = useFormValue(["_id"]);

const docId = typeof documentId === "string"
  ? documentId.replace("drafts.", "")
  : props.id;
Had to do a
typeof
in front of the replace in my case, as Typescript kept complaining it was a Type
unknown
, and I'm still relatively new to TS in general!
Apr 23, 2023, 9:05 PM
props.id
in this case returns the name of the field that you set in your schema, so ultimately I think it doesn't do anything to update the document as a whole. Otherwise would be a lot easier!
Apr 23, 2023, 9:06 PM
useFormValue
is ok when I need to change current field, in my case I need to change different field
Apr 24, 2023, 8:28 AM
here is the example what I am building
Apr 24, 2023, 8:29 AM
I want ‘Translate All’ fill all non-english terms to be changed
Apr 24, 2023, 8:29 AM
Yeah, the
useFormValue
allows you to get the document ID to update different fields in the same document
Apr 24, 2023, 8:36 AM
indeed, last question why its not setting new value
patch.execute([{ 'set': { path: pathSegments, value: 'testValue' } }])
Apr 24, 2023, 8:47 AM
so I have
 const { patch } = useDocumentOperation(docId!, props.schemaType.name);

  //<https://translation.googleapis.com/language/translate/v2?q=super> puper text that needs translation&target=uk&format=text&source=en&model=base&key=
  async function translateText() {

    if (source !== undefined || source !== 'undefined' || source !== '' || source !== null) {

      const res = await <http://axios.post|axios.post><TranslateRespose>(
        `<https://translation.googleapis.com/language/translate/v2?q=${source}&target=${locale}&format=text&source=en&model=base&key=${key}>`
      );

      const translatedText = res.data.data.translations[0].translatedText;

      onChange(PatchEvent.from(set(translatedText)))
    }
  }

  async function translateAllChildrenText() {
    languages.map(async (lang) => {
      var pathSegments = [...props.path];
      pathSegments[pathSegments.length - 1] = lang.id;

      patch.execute([{ 'set': { path: pathSegments, value: 'testValue' } }])
    })
  }

Apr 24, 2023, 8:48 AM
The set object should look something like this:
patch.execute([
  {
    set: {
      SCHEMA_FIELD_NAME: NEW_VALUE,
    }
  }
])
Not sure what your schema fields are called, so try setting
SCHEMA_FIELD_NAME: 'testValue'
Apr 24, 2023, 8:58 AM
sorry for stupid question but I have this structure
Apr 24, 2023, 9:09 AM
I was trying to use SCHEMA_FILED_NAME `name.uk
name[uk]
Apr 24, 2023, 9:09 AM
but its not settings up values
Apr 24, 2023, 9:09 AM
can you share the document schema you're updating?
Apr 24, 2023, 9:10 AM
sure
import {MdLibraryMusic as icon} from 'react-icons/md'
import {MdYoutubeSearchedFor as seoIcon} from 'react-icons/md'
import {defineField, defineType} from 'sanity'
import DurationInputComponent from '../components/DurationInputComponent'
import TranslateStringInputComponent from '../components/TranslateStringInputComponent'

export default defineType({
  name: 'track',
  title: 'Track',
  type: 'document',
  icon,
  groups: [
    {
      name: 'seo',
      title: 'SEO',
      icon: seoIcon,
    },
    {
      name: 'codes',
      title: 'Codes',
    },
    {
      name: 'metadata',
      title: 'Metadata',
    },
    {
      name: 'statistics',
      title: 'Stats',
    },
  ],
  fields: [
    defineField({
      name: 'name',
      title: 'Name',
      type: 'localeString',
      group: 'metadata',
      options: {
        languageFilter: true,
      },
      validation: (Rule) => Rule.required(),
    }),
    defineField({
      name: 'description',
      title: 'description',
      type: 'localeString',
      group: 'metadata',
      options: {
        languageFilter: true,
      },
      validation: (Rule) => Rule.required(),
    }),
    defineField({
      name: 'isFeatured',
      title: 'Is Featured',
      type: 'boolean',
      validation: (Rule) => Rule.required(),
    }),
    defineField({
      name: 'tags',
      title: 'Tags',
      type: 'array',
      // validation: (Rule) => Rule.required(),
      of: [{type: 'reference', to: [{type: 'searchTag'}]}],
    }),
    defineField({
      name: 'image',
      title: 'Poster Image',
      type: 'image',
      group: 'metadata',
      validation: (Rule) => Rule.required(),
      options: {
        hotspot: true,
      },
    }),
    defineField({
      name: 'file',
      title: 'File',
      type: 'file',
      validation: (Rule) => Rule.required(),
    }),
    defineField({
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'name',
        maxLength: 100,
      },
    }),
    defineField({
      name: 'tempo',
      title: 'Tempo',
      type: 'number',
      group: 'metadata',
      initialValue: 100,
      validation: (Rule) =>
        Rule.precision(0).positive() && Rule.max(200) && Rule.min(50) && Rule.required(),
    }),
    defineField({
      name: 'duration',
      title: 'Duration (seconds)',
      type: 'number',
      group: 'metadata',
      readOnly: true,
      components: {
        input: DurationInputComponent,
      },
    }),
    defineField({
      name: 'visitCount',
      title: 'Visit',
      type: 'number',
      group: 'statistics',
    }),
    defineField({
      name: 'playCount',
      title: 'Play',
      type: 'number',
      group: 'statistics',
    }),
    defineField({
      name: 'downloadCount',
      title: 'Download',
      type: 'number',
      group: 'statistics',
    }),
    defineField({
      name: 'buyCount',
      title: 'Buy',
      type: 'number',
      group: 'statistics',
    }),
    defineField({
      name: 'upc',
      title: 'UPC',
      type: 'string',
      validation: (Rule) => Rule.required(),
      group: 'codes',
    }),
    defineField({
      name: 'isrc',
      title: 'ISRC',
      type: 'string',
      validation: (Rule) => Rule.required(),
      group: 'codes',
    }),
    defineField({
      name: 'iswc',
      title: 'ISWC',
      type: 'string',
      validation: (Rule) => Rule.required(),
      group: 'codes',
    }),
    defineField({
      name: 'recordLabel',
      title: 'Record Label',
      type: 'string',
      validation: (Rule) => Rule.required(),
    }),
    defineField({
      name: 'artists',
      title: 'Artists',
      type: 'array',
      validation: (Rule) => Rule.required(),
      of: [{type: 'reference', to: [{type: 'artist'}]}],
    }),
    defineField({
      name: 'composers',
      title: 'Composers',
      type: 'array',
      validation: (Rule) => Rule.required(),
      of: [{type: 'reference', to: [{type: 'composer'}]}],
    }),
    defineField({
      name: 'sources',
      title: 'Sources',
      type: 'array',
      of: [{type: 'streamingService'}],
    }),
    defineField({
      name: 'moods',
      title: 'Moods',
      type: 'array',
      validation: (Rule) => Rule.required(),
      of: [{type: 'reference', to: [{type: 'mood'}]}],
    }),
    defineField({
      name: 'ganres',
      title: 'Ganres',
      type: 'array',
      validation: (Rule) => Rule.required(),
      of: [{type: 'reference', to: [{type: 'ganre'}]}],
    }),
    defineField({
      name: 'seo',
      title: 'SEO',
      type: 'pageSeo',
      group: 'seo',
      // validation: (Rule) => Rule.required(),
    }),
  ],
  preview: {
    select: {
      name: 'name',
      media: 'image',
      isFeatured: 'isFeatured',
    },
    prepare(selection) {
      const {name, media, isFeatured} = selection
      return {
        title: name['en'],
        media,
        subtitle: isFeatured ? '⭐️ Featured' : '',
      }
    },
  },
  initialValue: {
    isFeatured: false,
    recordLabel: 'lesfreemusic',
    sources: [
      {name: 'Patreon', icon: 'mdi:patreon'},
      {name: 'Spotify', icon: 'mdi:spotify'},
      {name: 'Apple Music', icon: 'cib:apple-music'},
      {name: 'iTunes', icon: 'simple-icons:itunes'},
      {name: 'Youtube', icon: 'uil:youtube'},
      {name: 'Youtube Music', icon: 'arcticons:youtube-music'},
      {name: 'Amazon Music', icon: 'arcticons:amazon-music'},
      {name: 'Amazon', icon: 'simple-icons:amazon'},
      {name: 'TikTok', icon: 'ic:baseline-tiktok'},
      {name: 'Instagram', icon: 'mdi:instagram'},
      {name: 'SoundCloud', icon: 'mdi:soundcloud'},
      {name: 'Deezer', icon: 'tabler:brand-deezer'},
    ],
    composers: [{_ref: '45688ba9-a315-400b-98b3-b8c1442b9aa4'}],
  },
})
Apr 24, 2023, 9:14 AM
import {defineType} from 'sanity'
import TranslateStringInputComponent from '../../components/TranslateStringInputComponent'
import {languages} from '../../supportedLanguages'

// const languages = [
//   {id: 'en', title: 'English', isDefault: true},
//   {id: 'uk', title: 'Ukrainian'},
//   {id: 'cs', title: 'Czech'},
//   {id: 'da', title: 'Danish'},
//   {id: 'de', title: 'German'},
//   {id: 'es', title: 'Spanish'},
//   {id: 'fr', title: 'French'},
//   {id: 'it', title: 'Italian'},
//   {id: 'hu', title: 'Hungarian'},
//   {id: 'nl', title: 'Dutch'},
//   {id: 'pl', title: 'Polish'},
//   {id: 'pt', title: 'Portuguese'},
//   {id: 'pt_BR', title: 'Portuguese (Brazil)'},
//   {id: 'fi', title: 'Finnish'},
//   {id: 'sv', title: 'Swedish'},
//   {id: 'tr', title: 'Turkish'},
//   {id: 'ru', title: 'Russian'},
//   {id: 'th', title: 'Thai'},
//   {id: 'ko', title: 'Korean'},
//   {id: 'ja', title: 'Japanese'},
//   {id: 'zh_CN', title: 'Chinese (Simplified)'},
//   {id: 'zh_TW', title: 'Chinese (Traditional)'},
//   {id: 'ar', title: 'Arabic'},
//   {id: 'he', title: 'Hebrew'},
//   {id: 'hi', title: 'Hindi'},
//   {id: 'id', title: 'Indonesian'},
//   {id: 'fil', title: 'Filipino'},
//   {id: 'vi', title: 'Vietnamese'},
// ]

export default defineType({
  name: 'localeString',
  title: 'Locale String',
  type: 'object',
  fieldsets: [
    {
      title: 'Translations',
      name: 'translations',
      options: {collapsible: true, collapsed: true},
    },
  ],
  // @ts-ignore
  fields: languages.map((lang) => ({
    name: lang.id,
    title: lang.title,
    type: 'string', // or `text`, etc
    fieldset: lang.isDefault ? null : 'translations',
    components: {
      input: TranslateStringInputComponent,
    },
  })),
})`
Apr 24, 2023, 9:15 AM
Just to make sure I'm reading that right, only name and description are using your localeString type, and that's where the translations should be populating?
Apr 24, 2023, 9:19 AM
yes
Apr 24, 2023, 9:21 AM
so I'm guessing a little bit here, would the following work?
patch.execute([
  {
    set: {
      name: {
        uk: 'test value',
      }
    }
  }
])

Apr 24, 2023, 9:25 AM
you made my day, if you need music for videos let me know I will give you a license for using over 900 tracks from my library
Apr 24, 2023, 9:26 AM
awesome, glad it worked out! 😄
Apr 24, 2023, 9:27 AM
user H
be my saver today I cant apply all patches I am trying to execute this
 const translations = await Promise.all(languages.map(async (lang) => {

      var pathSegments = [...props.path];
      pathSegments[pathSegments.length - 1] = lang.id;
      const path = pathSegments.join('.');

      const translation = lang.id === 'en' ? source : await translate(source, `${lang.id}`);

      console.log('test: ', path);

      const patch = {
        set: {
          name: {
            [lang.id]: translation
          }
        }
      };

      patches.push(patch);
      return translation;
    }));

    console.log('translations: ', translations);
    console.log('patches: ', patches);

    const res = patch.execute(patches)
Apr 24, 2023, 10:55 AM
even when I am trying to do this
const res = patch.execute([
      {
        set: {
          name: {
            uk: 'ukr'
          }
        }
      },
      {
        set: {
          name: {
            en: source
          }
        }
      },
      {
        set: {
          name: {
            de: 'de value'
          }
        }
      },
    ])
Apr 24, 2023, 10:55 AM
I’ve got only de filed updated and en is earased
Apr 24, 2023, 10:56 AM
I guess it would depend how you're doing the translations? Does it only translate one file at a time?
I'd say maybe populate and object as follows:

const nameTranslation = {
  uk: 'uk name',
  en: 'en name',
  de: 'de name',
}
and then run your set as:

set: {
  name: nameTranslation
}
and see if that works to update multiple fields?

Then you could populate the object with all the keys + translations, and pass it through as one patch?
Apr 24, 2023, 11:48 AM

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?