UseState initialState based on async value

Viewed 5104

I would like to have a hook that controls a select but its initial state can be defined by the url and an api call.

Here is the behaviour I would like to achieve :

  • The component gets a list of documents by calling asynchronously an api (result.value will be set only after some time)
  • A document's id can be set in the url, match.params.id extracts it if match isn't "false"
  • useState controls a select, if (after the api call) an id is defined in the url and this id is found in the list of documents, I would like the index to be set to this value

Here is what I tried (of course it is not working) :

const match = useRouteMatch('/document/:id') //react-router
const result = useAsync(api.getDocuments, []) //react-use
const [selectIndex, setSelectIndex] = useState(match && !(result.loading || result.error) && result.value.documents.findIndex(({_id}) => _id === match.params.id))

Basically I would like to have an async initialState for useState

I feel useEffect might be of use but I still don't really get how to use it

2 Answers

Use the combination of useEffect and useState to initialise a value asynchronously which you can change on ui.

import { useEffect, useState } from 'react'
import { useRouteMatch } from 'react-router-dom'

const App = () => {
  const [value, setValue] = useState()
  
   const { params } = useRouteMatch('/document/:id')
  
  useEffect(() => {
    someAsyncStuff(params.id).then(data => setValue(data))
  }, [params])
  
  return (
    <select
      value={value}
      onChange={({target: { value }}) => setValue(value)}>
      <option value={1}>one</option>
      <option value={2}>two</option>   
    </select>
  )
}

Just to mention, in 2022, if you'are using some bundler that allows top-level await, you can read the async value outside of the component, so

import { useEffect, useState } from 'react'

const someValue = await getSomeValue();//<=this
const App = () => {
  const [value, setValue] = useState(someValue)
}
Related