Help needed porting custom input component from V2 to V3 for Vimeo video async select

1 replies
Last updated: Jan 17, 2023
Hey! I need a little help porting a custom input component from V2 to V3. It's a Vimeo video async select.It's based on these great resources:

https://www.sanity.io/schemas/asynchronous-list-options-component-05f63a29
https://www.sanity.io/ui/docs/component/autocomplete#example-with-custom-rendering The last thing I need to do, and can't quite figure out how to, is to access an item's payload inside my
onChange
event... I'm a Vue guy so it might be a very easy React trick but I'm kinda stuck now...The approach I have in mind is access my payload object then create my object as per my schema and then use the
set()
to update my content lake.Here's my code so far. Any help will be appreciated
🙂 Thanks !

import {Card, Autocomplete, Flex, Box, Text} from '@sanity/ui'
import {SearchIcon} from '@sanity/icons'
import {BsPlayBtn} from 'react-icons/bs'
import {useCallback, useEffect, useState} from 'react'
import {set, unset} from 'sanity'
import axios from 'axios'

const params =
  '?fields=uri,created_time,name,description,link,pictures,files,width,height,duration&per_page=100'

const AsyncVimeoSelect = (props) => {
  const [listItems, setListItems] = useState([])
  const {onChange, value = '', id, focusRef, onBlur, onFocus, readOnly} = props

  const handleChange = useCallback(
    (event) => {
      // console.log(event)
      // const nextValue = event.currentTarget.value
      onChange(event ? set({name: event}) : unset())
    },
    [onChange]
  )

  useEffect(() => {
    const getItems = async () => {
      const items = await axios
        .get(
          `<https://api.vimeo.com/me/projects/${>
            import.meta.env.SANITY_STUDIO_VIMEO_FOLDER
          }/videos${params}`,
          {
            headers: {
              Authorization: `Bearer ${import.meta.env.SANITY_STUDIO_VIMEO_TOKEN}`,
            },
          }
        )
        .then((res) => {
          return res.data.data.map((video) => {
            return {value: video.name, payload: video}
          })
        })
      setListItems(items)
    }
    getItems()
  }, [])

  return (
    <Card padding={[3, 3, 4]} paddingBottom={[8, 8, 9]}>
      <Autocomplete
        filterOption={(query, option) =>
          option.payload.name.toLowerCase().indexOf(query.toLowerCase()) > -1
        }
        fontSize={[2, 2, 3]}
        icon={SearchIcon}
        openButton
        options={listItems}
        padding={[3, 3, 4]}
        placeholder="Type to find item …"
        renderOption={(option) => (
          <Card as="button">
            <Flex align="center">
              <Box paddingLeft={3} paddingY={2}>
                <BsPlayBtn />
              </Box>
              <Box flex={1} padding={3}>
                <Text size={[2, 2, 3]}>{option.payload.name}</Text>
              </Box>
            </Flex>
          </Card>
        )}
        renderValue={(value, option) => {
          return option.payload.name
        }}
        onChange={handleChange}
      />
    </Card>
  )
}

export default AsyncVimeoSelect
Jan 17, 2023, 10:51 AM
Little update. It's working now, But I don't know if using
import useState from 'react-usestateref'
is the most elegant solution 😜 ?

import {Card, Autocomplete, Flex, Box, Text, Stack} from '@sanity/ui'
import {SearchIcon} from '@sanity/icons'
import {BsPlayBtn} from 'react-icons/bs'
import useState from 'react-usestateref'
import {useCallback, useEffect} from 'react'
import {set, unset} from 'sanity'
import axios from 'axios'

const params =
  '?fields=uri,created_time,name,description,link,pictures,files,width,height,duration&per_page=100'

const AsyncVimeoSelect = (props) => {
  const [listItems, setListItems, itemsRef] = useState([])
  const {onChange, value = '', id, focusRef, onBlur, onFocus, readOnly} = props

  const handleChange = useCallback(
    (val) => {
      const item = val ? itemsRef.current.find((item) => (item.payload.uri = val)) : ''
      const itemObj = {...item?.payload}
      onChange(val ? set(itemObj) : unset())
    },
    [onChange]
  )

  useEffect(() => {
    const getItems = async () => {
      const items = await axios
        .get(
          `<https://api.vimeo.com/me/projects/${>
            import.meta.env.SANITY_STUDIO_VIMEO_FOLDER
          }/videos${params}`,
          {
            headers: {
              Authorization: `Bearer ${import.meta.env.SANITY_STUDIO_VIMEO_TOKEN}`,
            },
          }
        )
        .then((res) => {
          return res.data.data.map((video) => {
            return {value: video.uri, payload: video}
          })
        })
      setListItems(items)
    }
    getItems()
  }, [])

  return (
    <Autocomplete
      filterOption={(query, option) =>
        option.payload.name.toLowerCase().indexOf(query.toLowerCase()) > -1
      }
      fontSize={2}
      icon={SearchIcon}
      openButton
      options={listItems}
      padding={[2, 2, 3]}
      radius={0}
      placeholder="Type to find item …"
      renderOption={(option) => (
        <Card as="button">
          <Flex align="center">
            <Box paddingLeft={3} paddingY={2}>
              <BsPlayBtn />
            </Box>
            <Box flex={1} padding={3}>
              <Stack flex={1} space={2}>
                <Text size={2}>{option.payload.name}</Text>
                <Text muted size={1}>
                  {option.payload.uri}
                </Text>
              </Stack>
            </Box>
          </Flex>
        </Card>
      )}
      renderValue={(value, option) => {
        return option.payload.name
        // return (
        //   <Box flex={1} padding={3}>
        //     <Stack flex={1} space={2}>
        //       <Text size={2}>{option.payload.name}</Text>
        //       <Text muted size={1}>
        //         {option.payload.uri}
        //       </Text>
        //     </Stack>
        //   </Box>
        // )
      }}
      onChange={handleChange}
    />
  )
}

export default AsyncVimeoSelect
Jan 17, 2023, 11:59 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?