Skip to content
This repository has been archived by the owner on Apr 27, 2022. It is now read-only.

Commit

Permalink
fix: prevent infinite loop of ad hooks
Browse files Browse the repository at this point in the history
Fixes infinite render loop caused by comparing object with useEffect hook.

Change to use [use-deep-compare-effect](https://github.com/kentcdodds/use-deep-compare-effect) internally.

Refs: #54
  • Loading branch information
wjaykim committed Nov 20, 2021
1 parent b3b8b73 commit dbfdaa1
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 94 deletions.
5 changes: 4 additions & 1 deletion package.json
Expand Up @@ -122,5 +122,8 @@
"eslintIgnore": [
"node_modules/",
"lib/"
]
],
"dependencies": {
"use-deep-compare-effect": "^1.8.1"
}
}
19 changes: 7 additions & 12 deletions src/AppOpenAdProvider.tsx
@@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import useDeepCompareEffect from 'use-deep-compare-effect';

import AppOpenAd from './ads/AppOpenAd';
import AppOpenAdContext from './AppOpenAdContext';
Expand All @@ -17,17 +18,11 @@ const AppOpenAdProvider = ({
}: AppOpenAdProviderProps) => {
const [appOpenAd, setAppOpenAd] = useState<AppOpenAd | null>(null);

useEffect(() => {
if (!unitId) {
setAppOpenAd((prevAd) => {
if (prevAd) {
prevAd.destroy();
}
return null;
});
return;
}
setAppOpenAd(AppOpenAd.createAd(unitId, options));
useDeepCompareEffect(() => {
setAppOpenAd((prevAd) => {
prevAd?.destroy();
return unitId ? AppOpenAd.createAd(unitId, options) : null;
});
}, [unitId, options]);

return (
Expand Down
3 changes: 1 addition & 2 deletions src/hooks/useAppOpenAd.ts
Expand Up @@ -16,7 +16,6 @@ export default function useAppOpenAd(): Omit<AdHookReturns, 'reward'> {
'AppOpenAdProvider is not found. You should wrap your components with AppOpenProvider to use useAppOpenAd hook.'
);
}
const returns = useFullScreenAd(appOpenAdContext.appOpenAd);

return returns;
return useFullScreenAd(appOpenAdContext.appOpenAd);
}
69 changes: 32 additions & 37 deletions src/hooks/useFullScreenAd.ts
Expand Up @@ -21,6 +21,11 @@ export default function useFullScreenAd<
const [adPresentError, setAdPresentError] = useState<Error>();
const [reward, setReward] = useState<Reward>();

const adShowing = useMemo(
() => adPresented && !adDismissed,
[adPresented, adDismissed]
);

const initialize = () => {
setAdLoaded(false);
setAdPresented(false);
Expand All @@ -30,11 +35,6 @@ export default function useFullScreenAd<
setReward(undefined);
};

const adShowing = useMemo(
() => adPresented && !adDismissed,
[adPresented, adDismissed]
);

const load = useCallback(
(requestOptions?: RequestOptions) => {
if (ad) {
Expand All @@ -56,41 +56,36 @@ export default function useFullScreenAd<
initialize();
return;
}
const loadListener = ad.addEventListener('adLoaded', () => {
setAdLoaded(true);
setAdPresented(false);
});
const loadFailListener = ad.addEventListener(
'adFailedToLoad',
(error: Error) => setAdLoadError(error)
);
const presentListener = ad.addEventListener('adPresented', () => {
setAdPresented(true);
setAdDismissed(false);
});
const presentFailListener = ad.addEventListener(
'adFailedToPresent',
(error: Error) => setAdPresentError(error)
);
const dismissListener = ad.addEventListener('adDismissed', () => {
setAdDismissed(true);
setAdLoaded(false);
});
const isRewardedAd =
ad instanceof RewardedAd || ad instanceof RewardedInterstitialAd;
let rewardListener = isRewardedAd
? (ad as RewardedAd | RewardedInterstitialAd).addEventListener(
'rewarded',
(r: Reward) => setReward(r)
)
: undefined;
const listeners = [
ad.addEventListener('adLoaded', () => {
setAdLoaded(true);
setAdPresented(false);
}),
ad.addEventListener('adFailedToLoad', (error: Error) =>
setAdLoadError(error)
),
ad.addEventListener('adPresented', () => {
setAdPresented(true);
setAdDismissed(false);
}),
ad.addEventListener('adFailedToPresent', (error: Error) =>
setAdPresentError(error)
),
ad.addEventListener('adDismissed', () => {
setAdDismissed(true);
setAdLoaded(false);
}),
isRewardedAd
? (ad as RewardedAd | RewardedInterstitialAd).addEventListener(
'rewarded',
(r: Reward) => setReward(r)
)
: undefined,
];
return () => {
loadListener.remove();
loadFailListener.remove();
presentListener.remove();
presentFailListener.remove();
dismissListener.remove();
rewardListener?.remove();
listeners.forEach((listener) => listener?.remove());
};
}, [ad]);

Expand Down
22 changes: 8 additions & 14 deletions src/hooks/useInterstitialAd.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';
import useDeepCompareEffect from 'use-deep-compare-effect';

import InterstitialAd from '../ads/InterstitialAd';
import { AdHookReturns, FullScreenAdOptions } from '../types';
Expand All @@ -17,20 +18,13 @@ export default function useInterstitialAd(
const [interstitialAd, setInterstitialAd] = useState<InterstitialAd | null>(
null
);
const returns = useFullScreenAd(interstitialAd);

useEffect(() => {
if (!unitId) {
setInterstitialAd((prevAd) => {
if (prevAd) {
prevAd.destroy();
}
return null;
});
return;
}
setInterstitialAd(InterstitialAd.createAd(unitId, options));
useDeepCompareEffect(() => {
setInterstitialAd((prevAd) => {
prevAd?.destroy();
return unitId ? InterstitialAd.createAd(unitId, options) : null;
});
}, [unitId, options]);

return returns;
return useFullScreenAd(interstitialAd);
}
22 changes: 8 additions & 14 deletions src/hooks/useRewardedAd.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';
import useDeepCompareEffect from 'use-deep-compare-effect';

import RewardedAd from '../ads/RewardedAd';
import { AdHookReturns, FullScreenAdOptions } from '../types';
Expand All @@ -15,20 +16,13 @@ export default function useRewardedAd(
options?: FullScreenAdOptions
): AdHookReturns {
const [rewardedAd, setRewardedAd] = useState<RewardedAd | null>(null);
const returns = useFullScreenAd(rewardedAd);

useEffect(() => {
if (!unitId) {
setRewardedAd((prevAd) => {
if (prevAd) {
prevAd.destroy();
}
return null;
});
return;
}
setRewardedAd(RewardedAd.createAd(unitId, options));
useDeepCompareEffect(() => {
setRewardedAd((prevAd) => {
prevAd?.destroy();
return unitId ? RewardedAd.createAd(unitId, options) : null;
});
}, [unitId, options]);

return returns;
return useFullScreenAd(rewardedAd);
}
22 changes: 8 additions & 14 deletions src/hooks/useRewardedInterstitialAd.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';
import useDeepCompareEffect from 'use-deep-compare-effect';

import RewardedInterstitialAd from '../ads/RewardedInterstitialAd';
import { AdHookReturns, FullScreenAdOptions } from '../types';
Expand All @@ -16,20 +17,13 @@ export default function useRewardedInterstitialAd(
): AdHookReturns {
const [rewardedInterstitialAd, setRewardedInterstitialAd] =
useState<RewardedInterstitialAd | null>(null);
const returns = useFullScreenAd(rewardedInterstitialAd);

useEffect(() => {
if (!unitId) {
setRewardedInterstitialAd((prevAd) => {
if (prevAd) {
prevAd.destroy();
}
return null;
});
return;
}
setRewardedInterstitialAd(RewardedInterstitialAd.createAd(unitId, options));
useDeepCompareEffect(() => {
setRewardedInterstitialAd((prevAd) => {
prevAd?.destroy();
return unitId ? RewardedInterstitialAd.createAd(unitId, options) : null;
});
}, [unitId, options]);

return returns;
return useFullScreenAd(rewardedInterstitialAd);
}
20 changes: 20 additions & 0 deletions yarn.lock
Expand Up @@ -1467,6 +1467,13 @@
pirates "^4.0.0"
source-map-support "^0.5.16"

"@babel/runtime@^7.12.5":
version "7.16.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5"
integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==
dependencies:
regenerator-runtime "^0.13.4"

"@babel/runtime@^7.8.4":
version "7.14.8"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446"
Expand Down Expand Up @@ -3626,6 +3633,11 @@ deprecation@^2.0.0, deprecation@^2.3.1:
resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==

dequal@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d"
integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==

destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
Expand Down Expand Up @@ -8446,6 +8458,14 @@ url-parse-lax@^3.0.0:
dependencies:
prepend-http "^2.0.0"

use-deep-compare-effect@^1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/use-deep-compare-effect/-/use-deep-compare-effect-1.8.1.tgz#ef0ce3b3271edb801da1ec23bf0754ef4189d0c6"
integrity sha512-kbeNVZ9Zkc0RFGpfMN3MNfaKNvcLNyxOAAd9O4CBZ+kCBXXscn9s/4I+8ytUER4RDpEYs5+O6Rs4PqiZ+rHr5Q==
dependencies:
"@babel/runtime" "^7.12.5"
dequal "^2.0.2"

use-subscription@^1.0.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1"
Expand Down

0 comments on commit dbfdaa1

Please sign in to comment.