在React中获取相互依赖的API时如何避免竞争条件?

问题描述 投票:0回答:1

我正在尝试使用 React 的

react-select
自定义组件更新选择,由于获取数据时发生竞争条件,我无法为其填充所选值。我已经看过几个关于如何处理
useEffect
中的 fetch 调用以及如何处理它们的问题,但没有任何效果...不确定这是否与
fetch
API 中的竞争条件或承诺有关我处理不当。

AlergiaForm.component.tsx

import Select, { SingleValue} from "react-select";
import {useState, useEffect} from "react";
interface IOption {
  value: string | null;
  label: string;
  icon?: string;
}

interface IProduct { 
  id: number,
  title: string
}

const AlergiaForm = () => {
  const[products, setProducts] = useState(Array<IProduct>);
  const[product, setProduct] = useState<SingleValue<IOption> | null>(null);
  const[isLoading, setLoading] = useState(false);
  const[value, setValue] = useState<string | null | undefined>("");

  //Mapper
  const mapper = ():IOption[] => {
    return products.map(item => {
      return { value: item.id, label: item.title};
    }) as unknown as IOption[];
  }

   useEffect(() => {
    //Get product, like an edit
    const getProduct = () => {
      setLoading(true);
      fetch('https://dummyjson.com/products/6')
      .then(res => res.json())
      .then((data) => {
          //We get the products dropdown values and we are waiting for it... is not even async and I'm waiting on the promise I think?
          getProducts();

          setLoading(false);
          //Logic to "set" the current value, but since it has a race condition, it's not picking it up
          //Works when a hot module refresh happens in Vite because the context is updated already... but not on page "load" or react-router-dom navigation
          const product = data as IProduct;
          const {id} = {...product};
          const selectedProduct = products.find(i => i.id === id) as IProduct;
          const ioption = { value: selectedProduct?.id.toString(), label: selectedProduct?.title } as SingleValue<IOption>;
          setProduct(ioption);
      });
    }

    const getProducts = () => {
      fetch('https://dummyjson.com/products')
      .then(res => res.json())
      .then((data) => {
        const products = data.products as IProduct[];
        setProducts(products);
      });
    }             

    getProduct();

   }, []);

   const handleDropDownChange = (selectedOption: SingleValue<IOption>):void => {
    const { value } = {...selectedOption};
    setValue(value);
}

  return(
    <>
      <Select options={mapper()} value={isLoading ? null : product} onChange={handleDropDownChange} />
      <p>Value selected: {product?.value ?? value}</p>
      <p>Is Loading: {isLoading ? "Loading..." : "Done"}</p>
    </>
  )  
}

  export default AlergiaForm;

应用程序.tsx

import AlergiaForm from './AlergiaForm.component'

function App() {
  return (
    <main>
      <AlergiaForm />
    </main>
  )
}

export default App;

具有相同代码的最小示例: 示例

您会看到检索产品时它不会更新所选值,这可能是因为尝试设置产品时未完成承诺,但不确定如何避免这种情况,即使等待实际响应以获得数据并正确设置值。

另一件事要提到的是,我正在使用

Vite
,每次重新加载保存文件上的页面时,都会选择该值,所以我知道代码正在工作,只是我猜问题是这里的竞争条件。

文章阅读:https://maxrozen.com/fetching-data-react-with-useeffect
https://overreacted.io/a-complete-guide-to-useeffect/

reactjs typescript vite react-select react-typescript
1个回答
0
投票
  1. 要设置商品时先调用getProducts函数,这样就有商品了
      useEffect(() => {
        getProducts();
       }, []);
  1. 将产品作为参数传递给 getProduct 函数,因为 React 会异步更新状态。
    const getProducts = () => {
        fetch('https://dummyjson.com/products')
        .then(res => res.json())
        .then((data) => {
          const products = data.products as IProduct[];
          setProducts(products);
          getProduct(products)
        });
      }   

示例

© www.soinside.com 2019 - 2024. All rights reserved.