具有更新状态对象的 useCallback - React.js

useCallback with updated state object - React.js

我有一个 POST API 调用,我在单击按钮时进行调用。我们有一个大型状态对象,它作为 body 发送给 POST 调用。此状态对象会根据页面上的不同用户交互不断更新。

function QuotePreview(props) {
  const [quoteDetails, setQuoteDetails] = useState({});
  const [loadingCreateQuote, setLoadingCreateQuote] = useState(false);

  useEffect(() => {
    if(apiResponse?.content?.quotePreview?.quoteDetails) {
      setQuoteDetails(apiResponse?.content?.quotePreview?.quoteDetails);
    }
  }, [apiResponse]);

  const onGridUpdate = (data) => {
    let subTotal = data.reduce((subTotal, {extendedPrice}) => subTotal + extendedPrice, 0);
    subTotal = Math.round((subTotal + Number.EPSILON) * 100) / 100

    setQuoteDetails((previousQuoteDetails) => ({
      ...previousQuoteDetails,
      subTotal: subTotal,
      Currency: currencySymbol,
      items: data,
    }));
  };

  const createQuote = async () => {
    try {
      setLoadingCreateQuote(true);
      const result = await usPost(componentProp.quickQuoteEndpoint, quoteDetails);
      if (result.data?.content) {
        /** TODO: next steps with quoteId & confirmationId */
        console.log(result.data.content);
      }
      return result.data;
    } catch( error ) {
      return error;
    } finally {
      setLoadingCreateQuote(false);
    }
  };

  const handleQuickQuote = useCallback(createQuote, [quoteDetails, loadingCreateQuote]);

  const handleQuickQuoteWithoutDeals = (e) => {
    e.preventDefault();
    // remove deal if present
    if (quoteDetails.hasOwnProperty("deal")) {
      delete quoteDetails.deal;
    }
    handleQuickQuote();
  }

  const generalInfoChange = (generalInformation) =>{
    setQuoteDetails((previousQuoteDetails) => (
      {
        ...previousQuoteDetails,
        tier: generalInformation.tier,
      }
    ));
  }

  const endUserInfoChange = (endUserlInformation) =>{
    setQuoteDetails((previousQuoteDetails) => (
      {
        ...previousQuoteDetails,
        endUser: endUserlInformation,
      }
    ));
  }

  return (
    <div className="cmp-quote-preview">
      {/* child components [handleQuickQuote will be passed down] */}
    </div>
  );
}

handleQuickQuoteWithoutDeals 函数被调用时,我正在从对象中删除一个键。但我想立即用更新后的对象调用 API 。我在这里直接删除交易密钥,但如果我以不可变的方式进行删除,则以下 API 调用不会考虑更新的对象,而是考虑前一个对象。

我发现解决这个问题的唯一方法是引入一个新状态并在点击时更新它,然后利用 useEffect 挂钩来跟踪这个状态,以便在它出现时进行 API 调用变化。使用这种方法,它会以一种奇怪的方式工作,它会在初始加载时不断调用 API 以及其他奇怪的行为。

有更简洁的方法吗?

不清楚 children 将如何调用 handleQuickQuote 回调,但如果您需要在回调范围内关闭 quoteDetails 详细信息的“副本”,那么我建议进行以下小重构,以允许此 parent 组件使用原始 createQuote 函数,而 children 接收包含当前 quoteDetails 的记忆回调。

使用 quoteDetails 作为参数:

const createQuote = async (quoteDetails) => {
  try {
    setLoadingCreateQuote(true);
    const result = await usPost(componentProp.quickQuoteEndpoint, quoteDetails);
    if (result.data?.content) {
      /** TODO: next steps with quoteId & confirmationId */
      console.log(result.data.content);
    }
    return result.data;
  } catch( error ) {
    return error;
  } finally {
    setLoadingCreateQuote(false);
  }
};

记住传入 quoteDetails 值的“匿名”回调:

const handleQuickQuote = useCallback(
  () => createQuote(quoteDetails),
  [quoteDetails]
);

创建 quoteDetails 的浅表副本,删除 属性,然后调用 createQuote:

const handleQuickQuoteWithoutDeals = (e) => {
  e.preventDefault();
  const quoteDetailsCopy = { ...quoteDetails };

  // remove deal if present
  if (quoteDetailsCopy.hasOwnProperty("deal")) {
    delete quoteDetailsCopy.deal;
  }
  createQuote(quoteDetailsCopy);
}