Skip to content

Commit 312f008

Browse files
committed
Deduplicate useQueryState
1 parent 2124217 commit 312f008

File tree

1 file changed

+90
-261
lines changed

1 file changed

+90
-261
lines changed

packages/toolkit/src/query/react/buildHooks.ts

Lines changed: 90 additions & 261 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,7 @@ export type UseInfiniteQueryState<
911911
arg: QueryArgFrom<D> | SkipToken,
912912
options?: UseInfiniteQueryStateOptions<D, R>,
913913
) => UseInfiniteQueryStateResult<D, R>
914+
914915
export type TypedUseInfiniteQueryState<
915916
ResultType,
916917
QueryArg,
@@ -1556,6 +1557,89 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
15561557
return [promiseRef, dispatch, initiate, stableSubscriptionOptions] as const
15571558
}
15581559

1560+
function buildUseQueryState(
1561+
endpointName: string,
1562+
preSelector:
1563+
| typeof queryStatePreSelector
1564+
| typeof infiniteQueryStatePreSelector,
1565+
) {
1566+
const useQueryState = (
1567+
arg: any,
1568+
{
1569+
skip = false,
1570+
selectFromResult,
1571+
}:
1572+
| UseQueryStateOptions<any, any>
1573+
| UseInfiniteQueryStateOptions<any, any> = {},
1574+
) => {
1575+
const { select } = api.endpoints[endpointName] as ApiEndpointQuery<
1576+
QueryDefinition<any, any, any, any, any>,
1577+
Definitions
1578+
>
1579+
const stableArg = useStableQueryArgs(
1580+
skip ? skipToken : arg,
1581+
serializeQueryArgs,
1582+
context.endpointDefinitions[endpointName],
1583+
endpointName,
1584+
)
1585+
1586+
type ApiRootState = Parameters<ReturnType<typeof select>>[0]
1587+
1588+
const lastValue = useRef<any>(undefined)
1589+
1590+
const selectDefaultResult: Selector<ApiRootState, any, [any]> = useMemo(
1591+
() =>
1592+
// Normally ts-ignores are bad and should be avoided, but we're
1593+
// already casting this selector to be `Selector<any>` anyway,
1594+
// so the inconsistencies don't matter here
1595+
// @ts-ignore
1596+
createSelector(
1597+
[
1598+
select(stableArg),
1599+
(_: ApiRootState, lastResult: any) => lastResult,
1600+
(_: ApiRootState) => stableArg,
1601+
],
1602+
preSelector,
1603+
{
1604+
memoizeOptions: {
1605+
resultEqualityCheck: shallowEqual,
1606+
},
1607+
},
1608+
),
1609+
[select, stableArg],
1610+
)
1611+
1612+
const querySelector: Selector<ApiRootState, any, [any]> = useMemo(
1613+
() =>
1614+
selectFromResult
1615+
? createSelector([selectDefaultResult], selectFromResult, {
1616+
devModeChecks: { identityFunctionCheck: 'never' },
1617+
})
1618+
: selectDefaultResult,
1619+
[selectDefaultResult, selectFromResult],
1620+
)
1621+
1622+
const currentState = useSelector(
1623+
(state: RootState<Definitions, any, any>) =>
1624+
querySelector(state, lastValue.current),
1625+
shallowEqual,
1626+
)
1627+
1628+
const store = useStore<RootState<Definitions, any, any>>()
1629+
const newLastValue = selectDefaultResult(
1630+
store.getState(),
1631+
lastValue.current,
1632+
)
1633+
useIsomorphicLayoutEffect(() => {
1634+
lastValue.current = newLastValue
1635+
}, [newLastValue])
1636+
1637+
return currentState
1638+
}
1639+
1640+
return useQueryState
1641+
}
1642+
15591643
function buildQueryHooks(endpointName: string): QueryHooks<any> {
15601644
const useQuerySubscription: UseQuerySubscription<any> = (
15611645
arg: any,
@@ -1685,70 +1769,10 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
16851769
)
16861770
}
16871771

1688-
const useQueryState: UseQueryState<any> = (
1689-
arg: any,
1690-
{ skip = false, selectFromResult } = {},
1691-
) => {
1692-
const { select } = api.endpoints[endpointName] as ApiEndpointQuery<
1693-
QueryDefinition<any, any, any, any, any>,
1694-
Definitions
1695-
>
1696-
const stableArg = useStableQueryArgs(
1697-
skip ? skipToken : arg,
1698-
serializeQueryArgs,
1699-
context.endpointDefinitions[endpointName],
1700-
endpointName,
1701-
)
1702-
1703-
type ApiRootState = Parameters<ReturnType<typeof select>>[0]
1704-
1705-
const lastValue = useRef<any>(undefined)
1706-
1707-
const selectDefaultResult: Selector<ApiRootState, any, [any]> = useMemo(
1708-
() =>
1709-
createSelector(
1710-
[
1711-
select(stableArg),
1712-
(_: ApiRootState, lastResult: any) => lastResult,
1713-
(_: ApiRootState) => stableArg,
1714-
],
1715-
queryStatePreSelector,
1716-
{
1717-
memoizeOptions: {
1718-
resultEqualityCheck: shallowEqual,
1719-
},
1720-
},
1721-
),
1722-
[select, stableArg],
1723-
)
1724-
1725-
const querySelector: Selector<ApiRootState, any, [any]> = useMemo(
1726-
() =>
1727-
selectFromResult
1728-
? createSelector([selectDefaultResult], selectFromResult, {
1729-
devModeChecks: { identityFunctionCheck: 'never' },
1730-
})
1731-
: selectDefaultResult,
1732-
[selectDefaultResult, selectFromResult],
1733-
)
1734-
1735-
const currentState = useSelector(
1736-
(state: RootState<Definitions, any, any>) =>
1737-
querySelector(state, lastValue.current),
1738-
shallowEqual,
1739-
)
1740-
1741-
const store = useStore<RootState<Definitions, any, any>>()
1742-
const newLastValue = selectDefaultResult(
1743-
store.getState(),
1744-
lastValue.current,
1745-
)
1746-
useIsomorphicLayoutEffect(() => {
1747-
lastValue.current = newLastValue
1748-
}, [newLastValue])
1749-
1750-
return currentState
1751-
}
1772+
const useQueryState: UseQueryState<any> = buildUseQueryState(
1773+
endpointName,
1774+
queryStatePreSelector,
1775+
)
17521776

17531777
return {
17541778
useQueryState,
@@ -1794,15 +1818,6 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
17941818
const useInfiniteQuerySubscription: UseInfiniteQuerySubscription<any> = (
17951819
arg: any,
17961820
options = {},
1797-
// {
1798-
// refetchOnReconnect,
1799-
// refetchOnFocus,
1800-
// refetchOnMountOrArgChange,
1801-
// skip = false,
1802-
// pollingInterval = 0,
1803-
// skipPollingIfUnfocused = false,
1804-
// initialPageParam,
1805-
// } = {},
18061821
) => {
18071822
const [promiseRef, dispatch, initiate, stableSubscriptionOptions] =
18081823
useQuerySubscriptionCommonImpl<InfiniteQueryActionCreatorResult<any>>(
@@ -1811,128 +1826,6 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
18111826
options,
18121827
)
18131828

1814-
// const { initiate } = api.endpoints[
1815-
// endpointName
1816-
// ] as unknown as ApiEndpointInfiniteQuery<
1817-
// InfiniteQueryDefinition<any, any, any, any, any>,
1818-
// Definitions
1819-
// >
1820-
// const dispatch = useDispatch<ThunkDispatch<any, any, UnknownAction>>()
1821-
// const subscriptionSelectorsRef = useRef<
1822-
// SubscriptionSelectors | undefined
1823-
// >(undefined)
1824-
// if (!subscriptionSelectorsRef.current) {
1825-
// const returnedValue = dispatch(
1826-
// api.internalActions.internal_getRTKQSubscriptions(),
1827-
// )
1828-
1829-
// if (process.env.NODE_ENV !== 'production') {
1830-
// if (
1831-
// typeof returnedValue !== 'object' ||
1832-
// typeof returnedValue?.type === 'string'
1833-
// ) {
1834-
// throw new Error(
1835-
// `Warning: Middleware for RTK-Query API at reducerPath "${api.reducerPath}" has not been added to the store.
1836-
// You must add the middleware for RTK-Query to function correctly!`,
1837-
// )
1838-
// }
1839-
// }
1840-
1841-
// subscriptionSelectorsRef.current =
1842-
// returnedValue as unknown as SubscriptionSelectors
1843-
// }
1844-
// const stableArg = useStableQueryArgs(
1845-
// skip ? skipToken : arg,
1846-
// // Even if the user provided a per-endpoint `serializeQueryArgs` with
1847-
// // a consistent return value, _here_ we want to use the default behavior
1848-
// // so we can tell if _anything_ actually changed. Otherwise, we can end up
1849-
// // with a case where the query args did change but the serialization doesn't,
1850-
// // and then we never try to initiate a refetch.
1851-
// defaultSerializeQueryArgs,
1852-
// context.endpointDefinitions[endpointName],
1853-
// endpointName,
1854-
// )
1855-
// const stableSubscriptionOptions = useShallowStableValue({
1856-
// refetchOnReconnect,
1857-
// refetchOnFocus,
1858-
// pollingInterval,
1859-
// skipPollingIfUnfocused,
1860-
// })
1861-
1862-
// const lastRenderHadSubscription = useRef(false)
1863-
1864-
// const promiseRef = useRef<
1865-
// InfiniteQueryActionCreatorResult<any> | undefined
1866-
// >(undefined)
1867-
1868-
// let { queryCacheKey, requestId } = promiseRef.current || {}
1869-
1870-
// // HACK We've saved the middleware subscription lookup callbacks into a ref,
1871-
// // so we can directly check here if the subscription exists for this query.
1872-
// let currentRenderHasSubscription = false
1873-
// if (queryCacheKey && requestId) {
1874-
// currentRenderHasSubscription =
1875-
// subscriptionSelectorsRef.current.isRequestSubscribed(
1876-
// queryCacheKey,
1877-
// requestId,
1878-
// )
1879-
// }
1880-
1881-
// const subscriptionRemoved =
1882-
// !currentRenderHasSubscription && lastRenderHadSubscription.current
1883-
1884-
// usePossiblyImmediateEffect(() => {
1885-
// lastRenderHadSubscription.current = currentRenderHasSubscription
1886-
// })
1887-
1888-
// usePossiblyImmediateEffect((): void | undefined => {
1889-
// if (subscriptionRemoved) {
1890-
// promiseRef.current = undefined
1891-
// }
1892-
// }, [subscriptionRemoved])
1893-
1894-
// usePossiblyImmediateEffect((): void | undefined => {
1895-
// const lastPromise = promiseRef.current
1896-
// if (
1897-
// typeof process !== 'undefined' &&
1898-
// process.env.NODE_ENV === 'removeMeOnCompilation'
1899-
// ) {
1900-
// // this is only present to enforce the rule of hooks to keep `isSubscribed` in the dependency array
1901-
// console.log(subscriptionRemoved)
1902-
// }
1903-
1904-
// if (stableArg === skipToken) {
1905-
// lastPromise?.unsubscribe()
1906-
// promiseRef.current = undefined
1907-
// return
1908-
// }
1909-
1910-
// const lastSubscriptionOptions = promiseRef.current?.subscriptionOptions
1911-
1912-
// if (!lastPromise || lastPromise.arg !== stableArg) {
1913-
// lastPromise?.unsubscribe()
1914-
// const promise = dispatch(
1915-
// initiate(stableArg, {
1916-
// initialPageParam,
1917-
// subscriptionOptions: stableSubscriptionOptions,
1918-
// forceRefetch: refetchOnMountOrArgChange,
1919-
// }),
1920-
// )
1921-
1922-
// promiseRef.current = promise
1923-
// } else if (stableSubscriptionOptions !== lastSubscriptionOptions) {
1924-
// lastPromise.updateSubscriptionOptions(stableSubscriptionOptions)
1925-
// }
1926-
// }, [
1927-
// dispatch,
1928-
// initiate,
1929-
// refetchOnMountOrArgChange,
1930-
// stableArg,
1931-
// stableSubscriptionOptions,
1932-
// subscriptionRemoved,
1933-
// initialPageParam,
1934-
// ])
1935-
19361829
const subscriptionOptionsRef = useRef(stableSubscriptionOptions)
19371830
usePossiblyImmediateEffect(() => {
19381831
subscriptionOptionsRef.current = stableSubscriptionOptions
@@ -1995,72 +1888,8 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
19951888
}, [promiseRef, trigger, arg])
19961889
}
19971890

1998-
const useInfiniteQueryState: UseInfiniteQueryState<any> = (
1999-
arg: any,
2000-
{ skip = false, selectFromResult } = {},
2001-
) => {
2002-
const { select } = api.endpoints[
2003-
endpointName
2004-
] as unknown as ApiEndpointInfiniteQuery<
2005-
InfiniteQueryDefinition<any, any, any, any, any>,
2006-
Definitions
2007-
>
2008-
const stableArg = useStableQueryArgs(
2009-
skip ? skipToken : arg,
2010-
serializeQueryArgs,
2011-
context.endpointDefinitions[endpointName],
2012-
endpointName,
2013-
)
2014-
2015-
type ApiRootState = Parameters<ReturnType<typeof select>>[0]
2016-
2017-
const lastValue = useRef<any>(undefined)
2018-
2019-
const selectDefaultResult: Selector<ApiRootState, any, [any]> = useMemo(
2020-
() =>
2021-
createSelector(
2022-
[
2023-
select(stableArg),
2024-
(_: ApiRootState, lastResult: any) => lastResult,
2025-
(_: ApiRootState) => stableArg,
2026-
],
2027-
infiniteQueryStatePreSelector,
2028-
{
2029-
memoizeOptions: {
2030-
resultEqualityCheck: shallowEqual,
2031-
},
2032-
},
2033-
),
2034-
[select, stableArg],
2035-
)
2036-
2037-
const querySelector: Selector<ApiRootState, any, [any]> = useMemo(
2038-
() =>
2039-
selectFromResult
2040-
? createSelector([selectDefaultResult], selectFromResult, {
2041-
devModeChecks: { identityFunctionCheck: 'never' },
2042-
})
2043-
: selectDefaultResult,
2044-
[selectDefaultResult, selectFromResult],
2045-
)
2046-
2047-
const currentState = useSelector(
2048-
(state: RootState<Definitions, any, any>) =>
2049-
querySelector(state, lastValue.current),
2050-
shallowEqual,
2051-
)
2052-
2053-
const store = useStore<RootState<Definitions, any, any>>()
2054-
const newLastValue = selectDefaultResult(
2055-
store.getState(),
2056-
lastValue.current,
2057-
)
2058-
useIsomorphicLayoutEffect(() => {
2059-
lastValue.current = newLastValue
2060-
}, [newLastValue])
2061-
2062-
return currentState
2063-
}
1891+
const useInfiniteQueryState: UseInfiniteQueryState<any> =
1892+
buildUseQueryState(endpointName, infiniteQueryStatePreSelector)
20641893

20651894
return {
20661895
useInfiniteQueryState,

0 commit comments

Comments
 (0)