useState setter not working in useEffect with async API call
const AsyncSelect = React.forwardRef((props, ref) => {
const [sessions, setSessions] = useState([])
const {
type, // Schema information
value, // Current field value
readOnly, // Boolean if field is not editable
placeholder,
markers, // Markers including validation rules
presence, // Presence information for collaborative avatars
compareValue, // Value to check for "edited" functionality
onFocus, // Method to handle focus state
onBlur, // Method to handle blur state
onChange // Method to handle patch events
} = props
// Creates a change handler for patching data
const handleChange = React.useCallback(
// useCallback will help with performance
(event) => {
const inputValue = event.currentTarget.value // get current value
// if the value exists, set the data, if not, unset the data
onChange(PatchEvent.from(inputValue ? set(inputValue) : unset()))
},
[onChange]
)
const inputId = useId()
useEffect(() => {
const getSessions = async () => {
const data = await fetch("<https://api.call>")
const json = await data.json()
const tSessions = await setSessions(json.collection) //NOT WORKING!!!
}
getSessions()
.catch(console.error);
}, [])
return (
<FormField
description={type.description} // Creates description from schema
title={type.title} // Creates label from schema title
__unstable_markers={markers} // Handles all markers including validation
__unstable_presence={presence} // Handles presence avatars
compareValue={compareValue} // Handles "edited" status
inputId={inputId} // Allows the label to connect to the input field
>
<Card padding={0}>
<Stack>
<Select
id={inputId} // A unique ID for this input
fontSize={2}
padding={[3, 3, 4]}
space={[3, 3, 4]}
value={value} // Current field value
readOnly={readOnly} // If "readOnly" is defined make this field read only
onFocus={onFocus} // Handles focus events
onBlur={onBlur} // Handles blur events
ref={ref}
onChange={handleChange} // A function to call when the input value changes
>
<option value={''}>---</option>
{sessions.map((s) => ({ s.name})
</Select>
</Stack>
</Card>
</FormField>
)
})
export default AsyncSelectThis is a common React issue, not specifically a Sanity problem, but I can help! The issue is that setSessions is a state setter function, not an async function - it doesn't return a promise, so you shouldn't await it.
Here's the corrected code:
useEffect(() => {
const getSessions = async () => {
const data = await fetch("https://api.call")
const json = await data.json()
setSessions(json.collection) // Remove the await here!
}
getSessions().catch(console.error);
}, [])Why this happens:
setSessionsis a synchronous function that schedules a state update- It doesn't return a promise, so
awaitdoesn't make sense here - React batches state updates and applies them after your function completes
Additional improvements for your code:
- Fix the map syntax error - you have a syntax error in your map function:
{sessions.map((s) => (
<option key={s.id || s.name} value={s.name}>
{s.name}
</option>
))}- Add loading state to handle the async period:
const [sessions, setSessions] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
const getSessions = async () => {
try {
const data = await fetch("https://api.call")
const json = await data.json()
setSessions(json.collection)
} catch (error) {
console.error(error)
} finally {
setLoading(false)
}
}
getSessions()
}, [])- Consider using Sanity's built-in hooks - if you're fetching Sanity data, you might want to use hooks like
useLiveQueryinstead of raw fetch calls for better integration with the Studio.
The main fix is removing await from setSessions - that should get your component working!
Show original thread25 replies
Was this answer helpful?
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.