简体   繁体   English

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

[英]useCallback with updated state object - React.js

I have a POST API call that I make on a button click.我有一个POST API 调用,我在单击按钮时进行了调用。 We have one large state object that gets sent as body for a POST call.我们有一个大型状态对象,它作为POST调用的body发送。 This state object keeps getting updated based on different user interactions on the page.这个状态对象会根据页面上不同的用户交互不断更新。

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>
  );
}

when the handleQuickQuoteWithoutDeals function gets called, I am deleting a key from the object.handleQuickQuoteWithoutDeals函数被调用时,我正在从对象中删除一个键。 But I would like to immediately call the API with the updated object.但我想立即使用更新后的对象调用 API。 I am deleting the deal key directly here, but if I do it in an immutable way, the following API call is not considering the updated object but the previous one.我在这里直接删除交易键,但是如果我以不可变的方式执行此操作,则以下 API 调用不会考虑更新的对象,而是考虑前一个。

The only way I found around this was to introduce a new state and update it on click and then make use of the useEffect hook to track this state to make the API call when it changes.我发现解决此问题的唯一方法是引入一个新状态并在单击时更新它,然后使用useEffect钩子跟踪此状态以在它更改时进行 API 调用。 With this approach, it works in a weird way where it keeps calling the API on initial load as well and other weird behavior.使用这种方法,它以一种奇怪的方式工作,它在初始加载时不断调用 API 以及其他奇怪的行为。

Is there a cleaner way to do this?有没有更干净的方法来做到这一点?

It's not clear how any children would call the handleQuickQuote callback, but if you are needing to close over in callback scope a "copy" of the quoteDetails details then I suggest the following small refactor to allow this parent component to use the raw createQuote function while children receive a memoized callback with the current quoteDetails enclosed.目前尚不清楚任何子级将如何调用handleQuickQuote回调,但是如果您需要在回调范围内关闭quoteDetails详细信息的“副本”,那么我建议进行以下小重构以允许此父组件在同时使用原始createQuote函数孩子们收到一个带有当前quoteDetails的记忆回调。

Consume quoteDetails as an argument:使用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);
  }
};

Memoize an "anonymous" callback that passes in the quoteDetails value:记住传入quoteDetails值的“匿名”回调:

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

Create a shallow copy of quoteDetails , delete the property, and call createQuote :创建一个quoteDetails的浅拷贝,删除该属性,然后调用createQuote

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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