Issue with state setter not working inside useEffect async API call in a custom API component
25 replies
Last updated: Jun 21, 2022
O
Im creating a custom api calling input however the useState setter is not working inside useEffect async api call. Sorry for the long winded code message. Here’s my code if someone can assist:
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 AsyncSelectAI Update
This 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!
K
Hey Removed Name , can you please use a single message to describe your problem? This way we can use a single thread to discuss and the channel remains free in the meantime. 🙂
O
sure, sorry about that
O
can I use something like the
react-querypackage inside a sanity app?
O
been trying to get it to work for hours, this is painful
K
State setters are not aynchronous, so you can’t
await. They also don’t return anything, so you shouldn’t have to store it in a constant.
K
You can log your data after the
.json()call to check what you receive.
O
i can see what I receive, im getting the data, its just not being set to the sessions variable
O
any ideas?
K
Can you show a console log?
O
also getting this long erre
Check the render method of `ForwardRef`. See <https://fb.me/react-warning-keys> for more information.
in option (created by ForwardRef)
in ForwardRef (created by FormBuilderInputInner)
in ChangeIndicatorProvider (created by FormBuilderInputInner)
in div (created by FormBuilderInputInner)
in FormBuilderInputInner (created by FormBuilderInput)
in FormBuilderInput (created by ForwardRef(ObjectInputField))
in ConditionalHiddenField (created by ForwardRef(ObjectInputField))
in ForwardRef(ObjectInputField) (created by ForwardRef(ObjectInput))
in ConditionalReadOnlyContextProvider (created by ConditionalReadOnlyField)
in ConditionalReadOnlyField (created by ForwardRef(ObjectInput))
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Grid))
in ForwardRef(Grid) (created by ForwardRef(ObjectInput))
in ForwardRef(ObjectInput)
in ForwardRef(ObjectInput) (created by FormBuilderInputInner)
in ChangeIndicatorProvider (created by FormBuilderInputInner)
in div (created by FormBuilderInputInner)
in FormBuilderInputInner (created by FormBuilderInput)
in FormBuilderInput (created by SanityFormBuilder)
in ReviewChangesContextProvider (created by SanityFormBuilder)
in FormBuilderContext (created by SanityFormBuilderContext)
in SanityFormBuilderContext (created by SanityFormBuilder)
in SanityFormBuilder (created by FormView)
in form (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by FormView)
in div (created by ForwardRef(RegionsWithIntersections))
in div (created by styled.div)
in styled.div (created by ForwardRef(RegionsWithIntersections))
in ForwardRef(RegionsWithIntersections) (created by StickyOverlay)
in StickyOverlay (created by OverlayEnabled)
in Tracker (created by OverlayEnabled)
in OverlayEnabled (created by FormView)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Container))
in ForwardRef(Container) (created by FormView)
in FormView (created by DocumentPanel)
in div (created by ForwardRef(ScrollContainer))
in ForwardRef(ScrollContainer) (created by Styled(Component))
in Styled(Component) (created by DocumentPanel)
in BoundaryElementProvider (created by DocumentPanel)
in PortalProvider (created by DocumentPanel)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Card))
in Fe (created by ThemeProvider)
in ThemeProvider (created by ThemeColorProvider)
in ThemeColorProvider (created by ForwardRef(Card))
in ForwardRef(Card) (created by PaneContent__root)
in PaneContent__root (created by ForwardRef(PaneContent))
in ForwardRef(PaneContent) (created by DocumentPanel)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by DocumentPanel)
in DocumentPanel (created by InnerDocumentPane)
in div (created by ForwardRef(ScrollContainer))
in ForwardRef(ScrollContainer) (created by EnabledChangeConnectorRoot)
in Tracker (created by EnabledChangeConnectorRoot)
in EnabledChangeConnectorRoot (created by Styled(EnabledChangeConnectorRoot))
in Styled(EnabledChangeConnectorRoot) (created by InnerDocumentPane)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by InnerDocumentPane)
in DialogProvider (created by InnerDocumentPane)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by ForwardRef(Pane))
in BoundaryElementProvider (created by ForwardRef(Pane))
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Card))
in Fe (created by ThemeProvider)
in ThemeProvider (created by ThemeColorProvider)
in ThemeColorProvider (created by ForwardRef(Card))
in ForwardRef(Card) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Pane))
in LayerProvider (created by LegacyLayerProvider)
in LegacyLayerProvider (created by ForwardRef(Pane))
in ForwardRef(Pane) (created by KeyboardShortcutResponder)
in KeyboardShortcutResponder (created by GetHookCollectionState)
in GetHookCollectionState (created by RenderActionCollectionState)
in RenderActionCollectionState
in Unknown (created by InnerDocumentPane)
in InnerDocumentPane (created by DocumentPane)
in ReferenceInputOptionsProvider (created by DocumentPane)
in Unknown (created by DocumentPane)
in DocumentPane (created by DeskToolPane)
in PaneRouterProvider (created by DeskToolPane)
in DeskToolPane
in DeskToolPane
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Card))
in Fe (created by ThemeProvider)
in ThemeProvider (created by ThemeColorProvider)
in ThemeColorProvider (created by ForwardRef(Card))
in ForwardRef(Card) (created by Styled(Component))
in Styled(Component) (created by PaneLayout)
in PaneLayout (created by Styled(PaneLayout))
in Styled(PaneLayout)
in PortalProvider
in DeskToolProvider
in Unknown (created by DeskToolRoot)
in ErrorBoundary (created by DeskToolRoot)
in DeskToolRoot (created by RenderTool)
in ErrorBoundary (created by RenderTool)
in RenderTool (created by SchemaErrorReporter)
in RouteScope (created by SchemaErrorReporter)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by Styled(Component))
in Styled(Component) (created by SchemaErrorReporter)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by Styled(Component))
in Styled(Component) (created by SchemaErrorReporter)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by Styled(Component))
in Styled(Component) (created by SchemaErrorReporter)
in SchemaErrorReporter (created by DefaultLayout)
in DefaultLayout (created by DefaultLayoutRoot)
in RouterProvider (created by DefaultLayoutRoot)
in LoginWrapper (created by NormalizedLoginWrapper)
in NormalizedLoginWrapper (created by DefaultLayoutRoot)
in DefaultLayoutRoot (created by AppProvider)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Card))
in Fe (created by ThemeProvider)
in ThemeProvider (created by ThemeColorProvider)
in ThemeColorProvider (created by ForwardRef(Card))
in ForwardRef(Card) (created by Styled(Component))
in Styled(Component) (created by AppProvider)
in SnackbarProvider (created by AppProvider)
in ToastProvider (created by AppProvider)
in LayerProvider (created by AppProvider)
in PortalProvider (created by AppProvider)
in UserColorManagerProvider (created by AppProvider)
in AppProvider (created by SanityRoot)
in Fe (created by ThemeProvider)
in ThemeProvider (created by SanityRoot)
in ZIndexProvider (created by SanityRoot)
in SanityRoot
in AppContainerO
but im guessing it’s giving this warning because im mapping over the array and it’s empty
O
maybe use class component instead of hooks?
O
can i maybe fetch products ahead of time and save the array as json somewhere in the app?
S
Hi Removed Name , perhaps this guide by
user M
could help? 🙂 https://www.sanity.io/guides/asynchronous-list-options O
hey thanks Removed Name , I’ve been looking at that guide but for some reason, my state setter isn’t updating the same way as in the guide
O
trying to implement a class based component now, let’s see
S
The problem is that you’re not setting the state correctly, you can’t set state with await as Kitty explained. You need to do something like the following instead:
Hopefully your state should be populated with the colors from your api call, which you should see in your drop-down input
useEffect(() => {
const getSessions = async () => {
const data = await fetch('<https://api.call>')
.then(res => res.json())
.then(json => json.data.map(({ color }) => ({
title: color,
value: color.toLowerCase().split(' ').join('-')
})))
setSessions(data)
}
getSessions()
}, [])O
yea i fixed that and it still doesn’t set the state
K
but im guessing it’s giving this warning because im mapping over the array and it’s empty
No, it's totally fine to do that. Nothing would warn for that reason.
O
ok it’s working, im getting the data in the select as options and I can select/inspect it
O
however, im still getting this error:
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Check the render method of `FormBuilderInputInner`.
in CalendlyAsyncSelect (created by FormBuilderInputInner)
in ChangeIndicatorProvider (created by FormBuilderInputInner)
in div (created by FormBuilderInputInner)
in FormBuilderInputInner (created by FormBuilderInput)
in FormBuilderInput (created by ForwardRef(ObjectInputField))
in ConditionalHiddenField (created by ForwardRef(ObjectInputField))
in ForwardRef(ObjectInputField) (created by ForwardRef(ObjectInput))
in ConditionalReadOnlyContextProvider (created by ConditionalReadOnlyField)
in ConditionalReadOnlyField (created by ForwardRef(ObjectInput))
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Grid))
in ForwardRef(Grid) (created by ForwardRef(ObjectInput))
in ForwardRef(ObjectInput)
in ForwardRef(ObjectInput) (created by FormBuilderInputInner)
in ChangeIndicatorProvider (created by FormBuilderInputInner)
in div (created by FormBuilderInputInner)
in FormBuilderInputInner (created by FormBuilderInput)
in FormBuilderInput (created by SanityFormBuilder)
in ReviewChangesContextProvider (created by SanityFormBuilder)
in FormBuilderContext (created by SanityFormBuilderContext)
in SanityFormBuilderContext (created by SanityFormBuilder)
in SanityFormBuilder (created by FormView)
in form (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by FormView)
in div (created by ForwardRef(RegionsWithIntersections))
in div (created by styled.div)
in styled.div (created by ForwardRef(RegionsWithIntersections))
in ForwardRef(RegionsWithIntersections) (created by StickyOverlay)
in StickyOverlay (created by OverlayEnabled)
in Tracker (created by OverlayEnabled)
in OverlayEnabled (created by FormView)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Container))
in ForwardRef(Container) (created by FormView)
in FormView (created by DocumentPanel)
in div (created by ForwardRef(ScrollContainer))
in ForwardRef(ScrollContainer) (created by Styled(Component))
in Styled(Component) (created by DocumentPanel)
in BoundaryElementProvider (created by DocumentPanel)
in PortalProvider (created by DocumentPanel)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Card))
in Fe (created by ThemeProvider)
in ThemeProvider (created by ThemeColorProvider)
in ThemeColorProvider (created by ForwardRef(Card))
in ForwardRef(Card) (created by PaneContent__root)
in PaneContent__root (created by ForwardRef(PaneContent))
in ForwardRef(PaneContent) (created by DocumentPanel)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by DocumentPanel)
in DocumentPanel (created by InnerDocumentPane)
in div (created by ForwardRef(ScrollContainer))
in ForwardRef(ScrollContainer) (created by EnabledChangeConnectorRoot)
in Tracker (created by EnabledChangeConnectorRoot)
in EnabledChangeConnectorRoot (created by Styled(EnabledChangeConnectorRoot))
in Styled(EnabledChangeConnectorRoot) (created by InnerDocumentPane)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by InnerDocumentPane)
in DialogProvider (created by InnerDocumentPane)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by ForwardRef(Pane))
in BoundaryElementProvider (created by ForwardRef(Pane))
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Card))
in Fe (created by ThemeProvider)
in ThemeProvider (created by ThemeColorProvider)
in ThemeColorProvider (created by ForwardRef(Card))
in ForwardRef(Card) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Pane))
in LayerProvider (created by LegacyLayerProvider)
in LegacyLayerProvider (created by ForwardRef(Pane))
in ForwardRef(Pane) (created by KeyboardShortcutResponder)
in KeyboardShortcutResponder (created by GetHookCollectionState)
in GetHookCollectionState (created by RenderActionCollectionState)
in RenderActionCollectionState
in Unknown (created by InnerDocumentPane)
in InnerDocumentPane (created by DocumentPane)
in ReferenceInputOptionsProvider (created by DocumentPane)
in Unknown (created by DocumentPane)
in DocumentPane (created by DeskToolPane)
in PaneRouterProvider (created by DeskToolPane)
in DeskToolPane
in DeskToolPane
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Card))
in Fe (created by ThemeProvider)
in ThemeProvider (created by ThemeColorProvider)
in ThemeColorProvider (created by ForwardRef(Card))
in ForwardRef(Card) (created by Styled(Component))
in Styled(Component) (created by PaneLayout)
in PaneLayout (created by Styled(PaneLayout))
in Styled(PaneLayout)
in PortalProvider
in DeskToolProvider
in Unknown (created by DeskToolRoot)
in ErrorBoundary (created by DeskToolRoot)
in DeskToolRoot (created by RenderTool)
in ErrorBoundary (created by RenderTool)
in RenderTool (created by SchemaErrorReporter)
in RouteScope (created by SchemaErrorReporter)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by Styled(Component))
in Styled(Component) (created by SchemaErrorReporter)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by Styled(Component))
in Styled(Component) (created by SchemaErrorReporter)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Flex))
in ForwardRef(Flex) (created by Styled(Component))
in Styled(Component) (created by SchemaErrorReporter)
in SchemaErrorReporter (created by DefaultLayout)
in DefaultLayout (created by DefaultLayoutRoot)
in RouterProvider (created by DefaultLayoutRoot)
in LoginWrapper (created by NormalizedLoginWrapper)
in NormalizedLoginWrapper (created by DefaultLayoutRoot)
in DefaultLayoutRoot (created by AppProvider)
in div (created by styled.div)
in styled.div (created by ForwardRef(Box))
in ForwardRef(Box) (created by Styled(Component))
in Styled(Component) (created by ForwardRef(Card))
in Fe (created by ThemeProvider)
in ThemeProvider (created by ThemeColorProvider)
in ThemeColorProvider (created by ForwardRef(Card))
in ForwardRef(Card) (created by Styled(Component))
in Styled(Component) (created by AppProvider)
in SnackbarProvider (created by AppProvider)
in ToastProvider (created by AppProvider)
in LayerProvider (created by AppProvider)
in PortalProvider (created by AppProvider)
in UserColorManagerProvider (created by AppProvider)
in AppProvider (created by SanityRoot)
in Fe (created by ThemeProvider)
in ThemeProvider (created by SanityRoot)
in ZIndexProvider (created by SanityRoot)
in SanityRoot
in AppContainerO
IT WORKS!
O
wrap the component in React.useRef and pass ref to the select and error is gone
O
here is the code:
import React from 'react'
import fetch from 'node-fetch'
import PatchEvent, {set, unset} from 'part:@sanity/form-builder/patch-event'
import FormField from 'part:@sanity/components/formfields/default'
import DefaultSelect from 'part:@sanity/components/selects/default'
const CalendlyAsyncSelect = React.forwardRef((props, ref) => {
const unsetItem = {title: '', value: 'unset'}
const getSessions = async () => {
const data = await fetch(`<https://api.calendly.com/event_types?user=https://api.calendly.com/users/3a1c8403-dc60-438f-a50c-045471f1c4b9>`, {
"method": "GET",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2F1dGguY2FsZW5kbHkuY29tIiwiaWF0IjoxNjUyMDg2NDk0LCJqdGkiOiIyNjI5ZTg0OS01OGRmLTQyN2MtYjE1Zi05ZTY3MjJlYzkxNTgiLCJ1c2VyX3V1aWQiOiIzYTFjODQwMy1kYzYwLTQzOGYtYTUwYy0wNDU0NzFmMWM0YjkifQ.CSPb-TOXj5FuYSTOiN8Ga9o7EktCMOXd9fJuDXJVYLE"
}
})
.then(res => res.json())
.then(tsessions => tsessions.collection.map(({name, uri}) => {
return {
title: name, value: uri
}
})).then(data => setExperiments([unsetItem, ...data]))
}
const setValue = (item) => {
const {value} = item
const {onChange} = props
onChange(PatchEvent.from(value === 'unset' ? unset() : set(value)))
}
React.useEffect(() => {
getSessions()
}, [])
const [experiments, setExperiments] = React.useState([])
const {type, value} = props
const {title, description} = type
const currentValue = experiments.find(({value: optionVal}) => optionVal === value)
return (
<FormField label={title} description={description}>
<DefaultSelect ref={ref} value={currentValue} placeholder={'Select value'} items={experiments} onChange={setValue} />
</FormField>
)
})
export default CalendlyAsyncSelectO
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.