don't call onFetch when the fetch function returns null

This commit is contained in:
Michael Peters 2022-01-22 19:34:29 -06:00
parent 9a5e5d822e
commit 62c209a6fe
2 changed files with 26 additions and 21 deletions

View File

@ -26,7 +26,7 @@ const MemberList: FC<MemberListProps> = (props: MemberListProps) => {
if (!isNonNullAndHasValue(membersResult)) { if (!isNonNullAndHasValue(membersResult)) {
return <div className="members-loading">Loading members...</div> return <div className="members-loading">Loading members...</div>
} }
LOG.debug(`drawing ${membersResult.value.length} members`); //LOG.debug(`drawing ${membersResult.value.length} members`);
return membersResult.value.map((member: Member) => <MemberElement key={guild.id + member.id} guild={guild} member={member} memberGuild={membersResult.guild} />); return membersResult.value.map((member: Member) => <MemberElement key={guild.id + member.id} guild={guild} member={member} memberGuild={membersResult.guild} />);
}, [ guild, membersResult, membersFetchError ]); }, [ guild, membersResult, membersFetchError ]);

View File

@ -96,10 +96,11 @@ interface MultipleEventMappingParams<
* @param subscriptionParams Event callback functions * @param subscriptionParams Event callback functions
* @param fetchFunc Function that can be called to fetch the data for the subscription. This function will be called automatically if it is changed. * @param fetchFunc Function that can be called to fetch the data for the subscription. This function will be called automatically if it is changed.
* Typically, this function will be set up in a useCallback with a dependency on at least the guild. * Typically, this function will be set up in a useCallback with a dependency on at least the guild.
* If the fetch function returns null, this will not call "onFetch". This allows results to stay until the guilds are updated.
*/ */
function useGuildSubscriptionEffect<T>( function useGuildSubscriptionEffect<T>(
subscriptionParams: EffectParams<T>, subscriptionParams: EffectParams<T>,
fetchFunc: () => Promise<T> fetchFunc: () => Promise<T | null>
): [ fetchRetryCallable: () => Promise<void> ] { ): [ fetchRetryCallable: () => Promise<void> ] {
const { guild, onFetch, onFetchError, bindEventsFunc, unbindEventsFunc } = subscriptionParams; const { guild, onFetch, onFetchError, bindEventsFunc, unbindEventsFunc } = subscriptionParams;
@ -114,6 +115,7 @@ function useGuildSubscriptionEffect<T>(
const value = await fetchFunc(); const value = await fetchFunc();
if (!isMounted.current) return; if (!isMounted.current) return;
if (guildRef.current !== guild) return; // Don't even call onFetch if we changed guilds. TODO: Test this if (guildRef.current !== guild) return; // Don't even call onFetch if we changed guilds. TODO: Test this
if (!value) return; // we decided not to fetch, typically since there are conflicting guilds
onFetch(value, guild); onFetch(value, guild);
} catch (e: unknown) { } catch (e: unknown) {
LOG.error('error fetching for subscription', e); LOG.error('error fetching for subscription', e);
@ -152,7 +154,7 @@ function useGuildSubscriptionEffect<T>(
function useSingleGuildSubscription<T, UE extends keyof Connectable, CE extends keyof Conflictable>( function useSingleGuildSubscription<T, UE extends keyof Connectable, CE extends keyof Conflictable>(
guild: CombinedGuild, guild: CombinedGuild,
eventMappingParams: SingleEventMappingParams<T, UE, CE>, eventMappingParams: SingleEventMappingParams<T, UE, CE>,
fetchFunc: () => Promise<T> fetchFunc: () => Promise<T | null>
): [lastResult: SubscriptionResult<T> | null, fetchError: unknown | null] { ): [lastResult: SubscriptionResult<T> | null, fetchError: unknown | null] {
const { updatedEventName, updatedEventArgsMap, conflictEventName, conflictEventArgsMap } = eventMappingParams; const { updatedEventName, updatedEventArgsMap, conflictEventName, conflictEventArgsMap } = eventMappingParams;
@ -219,7 +221,7 @@ function useMultipleGuildSubscription<
>( >(
guild: CombinedGuild, guild: CombinedGuild,
eventMappingParams: MultipleEventMappingParams<T, NE, UE, RE, CE>, eventMappingParams: MultipleEventMappingParams<T, NE, UE, RE, CE>,
fetchFunc: () => Promise<T[]> fetchFunc: () => Promise<T[] | null>
): [ ): [
fetchRetryCallable: () => Promise<void>, fetchRetryCallable: () => Promise<void>,
lastResult: SubscriptionResult<T[]> | null, lastResult: SubscriptionResult<T[]> | null,
@ -337,7 +339,7 @@ function useMultipleGuildSubscriptionScrolling<
eventMappingParams: MultipleEventMappingParams<T, NE, UE, RE, CE>, eventMappingParams: MultipleEventMappingParams<T, NE, UE, RE, CE>,
maxElements: number, maxElements: number,
maxFetchElements: number, maxFetchElements: number,
fetchFunc: () => Promise<T[]>, fetchFunc: () => Promise<T[] | null>,
fetchAboveFunc: ((reference: T) => Promise<T[] | null>), fetchAboveFunc: ((reference: T) => Promise<T[] | null>),
fetchBelowFunc: ((reference: T) => Promise<T[] | null>), fetchBelowFunc: ((reference: T) => Promise<T[] | null>),
): [ ): [
@ -588,13 +590,14 @@ export function useGuildMetadataSubscription(guild: CombinedGuild) {
export function useResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null) { export function useResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null) {
const fetchResourceFunc = useCallback(async () => { const fetchResourceFunc = useCallback(async () => {
//LOG.silly('fetching resource for subscription (resourceId: ' + resourceId + ')'); //LOG.silly('fetching resource for subscription (resourceId: ' + resourceId + ')');
// Note: Returning null skips the load. This will prevent a null resourceResult
if (resourceId === null) return null; if (resourceId === null) return null;
if (resourceIdGuild === null) return null; if (resourceIdGuild === null) return null;
if (resourceIdGuild !== guild) return null; if (resourceIdGuild !== guild) return null;
const fetchResource = await guild.fetchResource(resourceId); const fetchResource = await guild.fetchResource(resourceId);
return fetchResource; return fetchResource;
}, [ guild, resourceIdGuild, resourceId ]); // Explicitly do NOT want lastFetchResource since it would cause a re-fetch after fetching successfully }, [ guild, resourceIdGuild, resourceId ]); // Explicitly do NOT want lastFetchResource since it would cause a re-fetch after fetching successfully
return useSingleGuildSubscription<Resource | null, 'update-resource', 'conflict-resource'>(guild, { return useSingleGuildSubscription<Resource, 'update-resource', 'conflict-resource'>(guild, {
updatedEventName: 'update-resource', updatedEventName: 'update-resource',
updatedEventArgsMap: (resource: Resource) => resource, updatedEventArgsMap: (resource: Resource) => resource,
conflictEventName: 'conflict-resource', conflictEventName: 'conflict-resource',
@ -604,7 +607,7 @@ export function useResourceSubscription(guild: CombinedGuild, resourceId: string
export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null): [ export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null, resourceIdGuild: CombinedGuild | null): [
imgSrc: string, imgSrc: string,
resourceResult: { value: Resource | null, guild: CombinedGuild } | null, resourceResult: SubscriptionResult<Resource> | null,
fetchError: unknown | null fetchError: unknown | null
] { ] {
const [ resourceResult, fetchError ] = useResourceSubscription(guild, resourceId, resourceIdGuild); const [ resourceResult, fetchError ] = useResourceSubscription(guild, resourceId, resourceIdGuild);
@ -613,7 +616,7 @@ export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resour
async () => { async () => {
//LOG.debug(`Fetching soft imgSrc for g#${guild.id} r#${resource?.id ?? '<null>'}`, { fetchError }); //LOG.debug(`Fetching soft imgSrc for g#${guild.id} r#${resource?.id ?? '<null>'}`, { fetchError });
if (fetchError) return './img/error.png'; if (fetchError) return './img/error.png';
if (!resourceResult || !resourceResult.value) return './img/loading.svg'; if (!resourceResult) return './img/loading.svg';
return await ElementsUtil.getImageSrcFromBufferFailSoftly(resourceResult.value.data); return await ElementsUtil.getImageSrcFromBufferFailSoftly(resourceResult.value.data);
}, },
'./img/loading.svg', './img/loading.svg',
@ -642,7 +645,9 @@ export function useChannelsSubscription(guild: CombinedGuild) {
export function useMembersSubscription(guild: CombinedGuild) { export function useMembersSubscription(guild: CombinedGuild) {
const fetchMembersFunc = useCallback(async () => { const fetchMembersFunc = useCallback(async () => {
return await guild.fetchMembers(); const members = await guild.fetchMembers();
return members;
//return await guild.fetchMembers();
}, [ guild ]); }, [ guild ]);
return useMultipleGuildSubscription<Member, 'new-members', 'update-members', 'remove-members', 'conflict-members'>(guild, { return useMultipleGuildSubscription<Member, 'new-members', 'update-members', 'remove-members', 'conflict-members'>(guild, {
newEventName: 'new-members', newEventName: 'new-members',
@ -658,26 +663,27 @@ export function useMembersSubscription(guild: CombinedGuild) {
} }
export function useSelfMemberSubscription(guild: CombinedGuild): [ export function useSelfMemberSubscription(guild: CombinedGuild): [
selfMemberResult: { value: Member | null selfMemberResult: SubscriptionResult<Member | null> | null
guild: CombinedGuild } | null
] { ] {
const [ _fetchRetryCallable, membersResult, _fetchError ] = useMembersSubscription(guild); const [ _fetchRetryCallable, membersResult, _fetchError ] = useMembersSubscription(guild);
// TODO: Show an error if we can't fetch and allow retry // TODO: Show an error if we can't fetch and allow retry
const selfMember = useMemo(() => { const [ selfMemberResult, setSelfMemberResult ] = useState<SubscriptionResult<Member | null> | null>(null);
if (membersResult && membersResult.value) {
useEffect(() => {
if (isNonNullAndHasValue(membersResult) && membersResult.guild === guild) {
const member = membersResult.value.find(m => m.id === guild.memberId); const member = membersResult.value.find(m => m.id === guild.memberId);
if (!member) { if (!member) {
LOG.warn('Unable to find self in members'); LOG.warn('unable to find self in members');
return null; setSelfMemberResult({ value: null, guild: membersResult.guild });
} else {
setSelfMemberResult({ value: member, guild: membersResult.guild });
} }
return member;
} }
return null; }, [ membersResult ]);
}, [ guild.memberId, membersResult ]);
return [ membersResult ? { value: selfMember, guild: membersResult.guild } : null ]; return [ selfMemberResult ];
} }
export function useTokensSubscription(guild: CombinedGuild) { export function useTokensSubscription(guild: CombinedGuild) {
@ -703,8 +709,7 @@ export function useMessagesScrollingSubscription(guild: CombinedGuild, channel:
const maxElements = Globals.MAX_CURRENT_MESSAGES; const maxElements = Globals.MAX_CURRENT_MESSAGES;
const fetchMessagesFunc = useCallback(async () => { const fetchMessagesFunc = useCallback(async () => {
if (guild !== channelGuild) { if (guild !== channelGuild) {
LOG.debug('not loading messages from guild differences'); return null; // Note: This skips the load so that we don't have to clear the message list
return [];
} }
return await guild.fetchMessagesRecent(channel.id, maxFetchElements); return await guild.fetchMessagesRecent(channel.id, maxFetchElements);
}, [ guild, channelGuild, channel.id, maxFetchElements ]); }, [ guild, channelGuild, channel.id, maxFetchElements ]);