0

UPDATE: Ok, it I misunderstood useDeferredValue, I thought it was more like a debounced value but it's not, you can define the timeout to be the time the old results will be shown.

So

const search = useDeferredValue(value, { timeoutMs: 10000 })

Gave me the desired effect, only it still show the warning right know.

Original

I want to have a search with the results below it, the search result should filter immediately based on the input of the text field. Then the query should be done debounced and the old results should show also when it takes less than e.g. 3000 m.s.

I'm working with the new concurrent mode in React and Relay experimental. I used the new useDeferredValue, documented on this page: https://reactjs.org/docs/concurrent-mode-reference.html#usetransition

But I got this warning:

Warning: Asynchronous triggered a user-blocking update that suspended.

The fix is to split the update into multiple parts: a user-blocking update to provide immediate feedback, and another update that triggers the bulk of the changes.

Refer to the documentation for useTransition to learn how to implement this pattern

I don't get this since it works but it still gives me a warning.

My code:

import React, {
  Suspense,
  useState,
  // @ts-ignore - useDeferredValue does not exist yet in types
  useDeferredValue,
  // @ts-ignore - useDeferredValue does not exist yet in types
  // useTransition,
  useCallback,
  ChangeEvent,
} from 'react'
import TextField from '@material-ui/core/TextField'
import LinearProgress from '@material-ui/core/LinearProgress'
import { graphql } from 'babel-plugin-relay/macro'
import { useLazyLoadQuery } from 'react-relay/hooks'
import {
  FlowBlockFinderQuery,
  FlowBlockFinderQueryResponse,
} from '../__generated__/FlowBlockFinderQuery.graphql'
import ErrorBoundaryWithRetry from '../helpers/ErrorBoundaryWithRetry'

interface RenderFuncProps {
  search: string
  filterSearch: string
}

function QueryResults({ search, filterSearch }: RenderFuncProps) {
  const { blocks }: FlowBlockFinderQueryResponse = useLazyLoadQuery<
    FlowBlockFinderQuery
  >(
    graphql`
      query FlowBlockFinderQuery($search: String) {
        blocks(search: $search) {
          id
          title
          description
          slug
          blockType
        }
      }
    `,
    { search },
    { fetchPolicy: 'store-or-network' }
  )
  return (
    <div>
      {blocks
        .filter(
          block =>
            !filterSearch ||
            block.title.toLowerCase().includes(filterSearch.toLowerCase())
        )
        .map(block => (
          <div key={block.id} style={{ fontSize: 19 }}>
            {block.title}
          </div>
        ))}
    </div>
  )
}
function Results({ search, filterSearch }: RenderFuncProps) {
  return (
    <>
      Zoekterm: {filterSearch}
      <ErrorBoundaryWithRetry
        fallback={({ error }) => <div>Er is iets foutgegaan</div>}
      >
        <Suspense fallback={<LinearProgress />}>
          <QueryResults search={search} filterSearch={filterSearch} />
        </Suspense>
      </ErrorBoundaryWithRetry>
    </>
  )
}

export default function Asynchronous() {
  const [value, setValue] = useState('')
  // const [search, setSearch] = useState('')
  const search = useDeferredValue(value, { timeoutMs: 3000 })
  // const [startTransition, isPending] = useTransition(SUSPENSE_CONFIG)
  const onInputChange = useCallback(
    (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
      // startTransition(() => {
      setValue(event.currentTarget.value)
      // })
    },
    [setValue]
  )

  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      <TextField
        label="Nieuw of bestaand blok"
        fullWidth
        variant="outlined"
        value={value}
        onChange={onInputChange}
      />
      <br />

      <Results search={search} filterSearch={value} />
    </div>
  )
}
Richard Lindhout
  • 2,038
  • 2
  • 23
  • 38

1 Answers1

0

React docs "if some state update causes a component to suspend, that state update should be wrapped in a transition". You have to make the async request suspense compatible and fetch the query in useTransition.

Here is an example from react docs

function handleChange(e) {
  const value = e.target.value;

  // Outside the transition (urgent)
  setQuery(value);

  startTransition(() => {
    // Inside the transition (may be delayed)
    setResource(fetchTranslation(value));
  });
 }

And the link to code sandbox

Rahil Ahmad
  • 3,056
  • 1
  • 16
  • 21