简体   繁体   中英

infinite loop in useEffect

I'm trying to create an autoselect if it's only one element in the options. Unfortunately, the option data from the server is passes several times, which makes the code slow and stops working at some point. is there any way to use only the final value of the options without intermediate ones? and how it can be done

export const FormikSelect: FC<IFormikSelect & FieldProps> = ({
  field,
  form,
  options,
  other = [],
  placeholder,
  isFilterable,
  isMulti,
  isClearable,
  filterStyle,
}) => {
  const {t} = useTranslation()
  const {width} = useWindowSize()
  const {name, value} = field
  const {setFieldValue, errors, setFieldError} = form
  const error = get(errors, name)
  const prevOptionsRef = useRef(options)
  const [currentOptions, setCurrentOptions] = useState(() => {
    if (!isEmpty(other)) {
      if (value && other.find((o) => o.value === value.value)) {
        return [...options, ...other]
      }
      return [...options, {label: t('OTHER'), value: 'other_value_button'}]
    }
    return options
  })

  useEffect(() => {
    if (options.length === 1) {
      console.log('options:', options)
      setFieldValue(name, options[0])
      if (error) setFieldError(name, undefined)
    }
  }, [options])

i tried to use useMemo but it didn't work. also i tried using [] in useEffect dependencies but that didn't work either. maybe there is some non-obvious way to solve the problem

I think your issue is that your options is a new array at each render. So you can either wrap it with useMemo before passing it to your Select , or changing the dependency array of your use effect to have only primitives

const isOnlyOneOption = options.length === 1;
const [firstOption] = options;
useEffect(() => ..., [isOnlyOneOption, firstOption])

Note that firstOption should be a primitive, not an object, otherwise you'll run into same schema. So maybe you'll need only to pass firstOption.value to your useEffect

You can also use the useRef hook to store the previous value of options and compare it to the current value in the effect. If the values are the same, you can skip the update. Here's an example of how you can do this:

const prevOptionsRef = useRef(options)

useEffect(() => {
  if (prevOptionsRef.current !== options) {
    prevOptionsRef.current = options
    if (options.length === 1) {
      console.log('options:', options)
      setFieldValue(name, options[0])
      if (error) setFieldError(name, undefined)
    }
  }
}, [options])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM