The-Commit-Company / frappe-react-sdk

React hooks for Frappe
https://frappe-react.vercel.app
MIT License
107 stars 36 forks source link

How to avoid unnecessary API calls whenever React re-renders in frappe-react-sdk hooks #48

Closed Shanuka-98 closed 9 months ago

Shanuka-98 commented 9 months ago

Hello,

I'm seeking guidance on how to prevent unnecessary API calls triggered during React re-renders within frappe-react-sdk hooks. This issue arises due to the lack of conditional rendering support in React hooks. Specifically, I'm trying to avoid unnecessary API calls within the useFrappeGetDocList hook using conditional fetching. In my case, I have added a filter to the hook and I only want to fetch when that filter state changes. I wonder if there exists a feasible approach within frappe-react-sdk hooks or if it requires additional implementation?

nikkothari22 commented 9 months ago

Hey @Shanuka-98

Frappe React SDK uses useSWR internally and exposes all properties through it's hooks.

In your case, changing the filter will make a new API call and update the data. If you don't want to make any API call when there's no filter applied, you can pass null to the key prop. When there's a filter applied, just pass undefined as the key.

You can read SWR's documentation for this. Frappe React SDK allows you to pass a key (pass null to not fetch, pass undefined to fetch, or pass a custom string for a custom key), and also allows you to pass SWR Configuration parameters as an object.

If you have any more questions, please send a code sample here so that we can help better. Thanks.

Shanuka-98 commented 9 months ago

Hey @nikkothari22

Here is a sample code, I just want to make a new API call only when isPatternMatch is true. Thanks!

import { useFrappeGetDocList } from "frappe-react-sdk";

const useIsExistingCustomer = (contactNumber) => {
  const contactNumberPattern = /^0\d{9}$/;
  const isPatternMatch = contactNumberPattern.test(contactNumber);

  const { data: customerData } = useFrappeGetDocList("Customer", {
    filters: [
      ["disabled", "=", 0],
      ["customer_mobile", "=", contactNumber],
    ],
  });

  // return true if customer is existing else return false
  return customerData?.length > 0;
};
export default useIsExistingCustomer;
nikkothari22 commented 9 months ago

The SWR Key can be set to null when isPatternMatch is not true. Otherwise, just pass undefined to use the standard key.

import { useFrappeGetDocList } from "frappe-react-sdk";

const useIsExistingCustomer = (contactNumber) => {
  const contactNumberPattern = /^0\d{9}$/;
  const isPatternMatch = contactNumberPattern.test(contactNumber);

  const { data: customerData } = useFrappeGetDocList("Customer", {
    filters: [
      ["disabled", "=", 0],
      ["customer_mobile", "=", contactNumber],
    ],
  }, isPatternMatch ? undefined: null);

  // return true if customer is existing else return false
  return customerData?.length > 0;
};
export default useIsExistingCustomer;

Also, in some cases, it would be better to not use the hooks but use the methods in frappe-js-sdk directly. Here's an example:


import { FrappeContext } from 'frappe-react-sdk';

export const MyComponent = (props) => {
  const { db } = useContext(FrappeContext)

  const getData = () => {
       db.getDocList(/** pass params here **/)
  }

return <button onClick={getData}>Get Data</button>
}
Shanuka-98 commented 9 months ago

Thank you so much, @nikkothari22, for your invaluable help. I truly appreciate it! Your guidance has been incredibly helpful. Also, thanks for sharing that alternative method using frappe-js-sdk. Sometimes, direct method implementation offers flexibility for specific functionalities that might not be available through hooks. Thank you once again for your support!