Refreshing and Refetching (part 1)
Refreshing Queries
Refreshing Queries (์ฟผ๋ฆฌ ์๋ก๊ณ ์นจ) ์ ์๋ฒ์์ ํด๋น ๋ฐ์ดํฐ์ ์ต์ ๋ฒ์ ์ ์ป๊ธฐ ์ํด ์๋ ์ฟผ๋ฆฌ์ ์ํด ๋ ๋๋ง๋ ๊ฒ๊ณผ ๋๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ ๋๋ค.
real-time features(์ค์๊ฐ ๊ธฐ๋ฅ๋ค) ์ด์ฉํ๊ธฐ
์๋ฒ์ ์ต์ ๋ฒ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ต์ ์ํ๋ก ์ ์ง ํ๊ธฐ์ํด์ ๊ฐ์ฅ ๋จผ์ ๊ณ ๋ คํ ์ฌํญ์ ์๋์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐ์ดํธ์์ผ์ฃผ๊ณ ์ฃผ๊ธฐ์ ์ผ๋ก ์๋ก๊ณ ์นจํด์ฃผ๋ ์ค์๊ฐ ๊ธฐ๋ฅ๋ค์ ์ด์ฉํ๋ ๊ฒ์ด ์ ์ ํ์ง ์ฌ๋ถ์ ๋๋ค.
real-time features(์ค์๊ฐ ๊ธฐ๋ฅ๋ค) ์ ํ ๊ฐ์ง ์๋ก๋ ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์๊ฒ ํด๋น ์ฟผ๋ฆฌ์ ์ ๋ณด๊ฐ ๋ฐ๋๋ ๋ง๋ค ์๋ ค์ฃผ๋ GQLS(GraphQL Subscriptions) ๊ฐ ์์ต๋๋ค. - ์๋ฒ๋ ๋คํธ์ํฌ ๋ ์ด์ด์ ์ถ๊ฐ์ ์ธ ์ค์ ์ด ํ์ํจ
useQueryLoader
/ loadQuery
๋ฅผ ์ฌ์ฉ
useQueryLoader
/ loadQuery
๋ฅผ ์ฌ์ฉuseQueryLoader ํ ์ ์ฌ์ฉํด์ ์ฟผ๋ฆฌ๋ฅผ ์๋ก๊ณ ์นจํ๊ธฐ ์ํด์๋ loadQuery ํจ์๋ง ๋ค์ ํธ์ถํ๋ฉด ๋๋ค.
/**
* App.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const [queryRef, loadQuery] = useQueryLoader<AppQueryType>(
AppQuery,
props.appQueryRef /* initial query ref */
);
const refresh = useCallback(() => {
// Load the query again using the same original variables.
// Calling loadQuery will update the value of queryRef.
// The fetchPolicy ensures we always fetch from the server and skip
// the local data cache.
const {variables} = props.appQueryRef;
loadQuery(variables, {fetchPolicy: 'network-only'});
}, [/* ... */]);
return (
<React.Suspense fallback="Loading query...">
<MainContent
refresh={refresh}
queryRef={queryRef}
/>
</React.Suspense>
);
}
/**
* MainContent.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
// Renders the preloaded query, given the query reference
function MainContent(props) {
const {refresh, queryRef} = props;
const data = usePreloadedQuery<AppQueryType>(
graphql`
query AppQuery($id: ID!) {
user(id: $id) {
name
friends {
count
}
}
}
`,
queryRef,
);
return (
<>
<h1>{data.user?.name}</h1>
<div>Friends count: {data.user.friends?.count}</div>
<Button
onClick={() => refresh()}>
Fetch latest count
</Button>
</>
);
}
์ ์์ ๋ฅผ ์ดํด๋ณด๋ฉด,
์ฐ๋ฆฌ๋ loadQuery ํจ์๋ฅผ ์๋ก๊ณ ์นจ์ ์ํ ์ด๋ฒคํธ ํธ๋ค๋ฌ ์์์ ํธ์ถํ๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ request ๋ ์ฆ์ ์์๋๊ณ ์ ๋ฐ์ดํธ๋ queryRef๋ฅผ usePreloaded๋ฅผ ์ฌ์ฉํ๋ MainContent ์ปดํฌ๋ํธ์ ์ ๋ฌํฉ๋๋ค.- ์ ๋ฐ์ดํธ๋ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํฉ๋๋ค.
ํญ์ ๋คํธ์ํฌ์์ ๊ฐ์ ธ์ค๊ณ ๋ก์ปฌ ๋ฐ์ดํฐ ์บ์๋ฅผ ๊ฑด๋๋ฐ๋๋ก ํ๊ธฐ ์ํด 'network-only' ๋ผ๋
fetchPolicy
๋ฅผ ์ ๋ฌ ํฉ๋๋ค.loadQuery ๋ฅผ ํธ์ถํ๋ ๊ฒ์ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋๋ฉด์ ๋ ๋๋ง์ ๋ง๋ usePreloadedQuery ๊ฐ ๋ฐ์ํฉ๋๋ค. ์ฐ๋ฆฌ๋ ๋ก๋ฉ์ํ๋ฅผ ํํํ๊ธฐ ์ํด์ Suspense boundary๋ก MainContent ์ปดํฌ๋ํธ๊ฐ ๊ฐ์ธ์ ธ์๋์ง ์ดํด๋ด์ผํฉ๋๋ค.
๋ง์ฝ Suspense ๋ฅผ ํผํด์ผํ๋ค๋ฉด
๊ฒฝ์ฐ์ ๋ฐ๋ผ Suspense ์ fallback ์ ๋ณด์ฌ์ฃผ์ง ์๊ณ ์ถ์ ๋๊ฐ ์์ต๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ์๋ ๋ก๋ฉ์ํ๋ฅผ ์๋์ผ๋ก ์ถ์ ํ ์์๋ fetchQuery ๋ฅผ ๋์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ฐธ๊ณ
๋์ ๋ ๋๋ง์ด ์ง์๋๋ ์ดํ์ React ๋ฒ์ ์์๋ ์ด ๊ฒฝ์ฐ์ ๋ํด์ ์ง์ํ ๊ฒ์ ๋๋ค Suspending ์์ ์ด๋ฏธ ๋ฆฌ๋ ๋๋ง๋ ๋ด์ฉ๋ค์ด Suspense fallback ์ผ๋ก ์จ๊ฒจ์ง๋ ๊ฒ์ ํผํ๋๋ก ํด์ค ๊ฒ์ ๋๋ค.
/**
* App.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const environment = useRelayEnvironment();
const [queryRef, loadQuery] = useQueryLoader<AppQueryType>(
AppQuery,
props.appQueryRef /* initial query ref */
);
const [isRefreshing, setIsRefreshing] = useState(false)
const refresh = useCallback(() => {
if (isRefreshing) { return; }
const {variables} = props.appQueryRef;
setIsRefreshing(true);
// fetchQuery will fetch the query and write
// the data to the Relay store. This will ensure
// that when we re-render, the data is already
// cached and we don't suspend
fetchQuery(environment, AppQuery, variables)
.subscribe({
complete: () => {
setIsRefreshing(false);
// *After* the query has been fetched, we call
// loadQuery again to re-render with a new
// queryRef.
// At this point the data for the query should
// be cached, so we use the 'store-only'
// fetchPolicy to avoid suspending.
loadQuery(variables, {fetchPolicy: 'store-only'});
}
error: () => {
setIsRefreshing(false);
}
});
}, [/* ... */]);
return (
<React.Suspense fallback="Loading query...">
<MainContent
isRefreshing={isRefreshing}
refresh={refresh}
queryRef={queryRef}
/>
</React.Suspense>
);
}
์ ์์ ๋ฅผ ์ดํด๋ณด๋ฉด,
์ฐ๋ฆฌ๊ฐ ์๋ก๊ณ ์นจํ ๋, ์ด์ ๋ ์ฐ๋ฆฌ์ isRefreshing ๋ก๋ฉ ์ํ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ์ด ์ํ๋ฅผ ์ฌ์ฉํด์ MainContent ๊ตฌ์ฑ ์์ ๋ด์์ MainContent๋ฅผ ์จ๊ธฐ์ง ์๊ณ ์ฌ์ฉ ์ค์ธ ์คํผ๋ ๋๋ ์ ์ฌํ ๋ก๋ฉ UI๋ฅผ ๋ ๋๋ง ํ ์ ์์ต๋๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ๋จผ์ fetchQuery๋ฅผ ํธ์ถํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๋ก์ปฌ ๋ฆด๋ ์ด ์ ์ฅ์์ ๋ฐ์ดํฐ๋ฅผ ์๋๋ค. fetchQuery ๋คํธ์ํฌ ์์ฒญ์ด ์๋ฃ๋๋ฉด loadQuery๋ฅผ ํธ์ถํ์ฌ ์ ๋ฐ์ดํธ๋ queryRef๋ฅผ ์ป์ ๋ค์ usePreloaded๋ก ์ ๋ฌํฉ๋๋ค.์ฟผ๋ฆฌ๋ ์ด์ ์์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ๋ฐ์ดํธ๋ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํฉ๋๋ค.
์ด๋ loadQuery๊ฐ ํธ์ถ๋๋ฉด ์ฟผ๋ฆฌ์ ๋ํ ๋ฐ์ดํฐ๊ฐ ๋ก์ปฌ ๋ฆด๋ ์ด ์ ์ฅ์์ ์ด๋ฏธ ์บ์๋์ด ์์ด์ผ ํ๋ฏ๋ก ์ผ์ ์ค๋จ์ ๋ฐฉ์งํ๊ณ ์ด๋ฏธ ์บ์๋ ๋ฐ์ดํฐ๋ง ์ฝ๊ธฐ ์ํด 'store-only'์ fetchPolicy๋ฅผ ์ฌ์ฉํฉ๋๋ค.
useLazyLoadQuery
๋ฅผ ์ฌ์ฉ
useLazyLoadQuery
๋ฅผ ์ฌ์ฉuseLazyLoadQuery Hook์ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ์๋ก ๊ณ ์น๋ ค๋ฉด ์๋์ ๊ฐ์ด ์ํํ ์ ์์ต๋๋ค.
/**
* App.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const variables = {id: '4'};
const [refreshedQueryOptions, setRefreshedQueryOptions] = useState(null);
const refresh = useCallback(() => {
// Trigger a re-render of useLazyLoadQuery with the same variables,
// but an updated fetchKey and fetchPolicy.
// The new fetchKey will ensure that the query is fully
// re-evaluated and refetched.
// The fetchPolicy ensures that we always fetch from the network
// and skip the local data cache.
setRefreshedQueryOptions(prev => ({
fetchKey: (prev?.fetchKey ?? 0) + 1,
fetchPolicy: 'network-only',
}));
}, [/* ... */]);
return (
<React.Suspense fallback="Loading query...">
<MainContent
refresh={refresh}
queryOptions={refreshedQueryOptions ?? {}}
variables={variables}
/>
</React.Suspense>
);
/**
* MainContent.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
// Fetches and renders the query, given the fetch options
function MainContent(props) {
const {refresh, queryOptions, variables} = props;
const data = useLazyLoadQuery<AppQueryType>(
graphql`
query AppQuery($id: ID!) {
user(id: $id) {
name
friends {
count
}
}
}
`,
variables,
queryOptions,
);
return (
<>
<h1>{data.user?.name}</h1>
<div>Friends count: {data.user.friends?.count}</div>
<Button
onClick={() => refresh()}>
Fetch latest count
</Button>
</>
);
}
์ ์์ ๋ฅผ ์ดํด๋ณด๋ฉด,
์ํ์์์ ์ ์ต์ ์ ์ค์ ํ์ฌ ์๋ก ๊ณ ์น ์ ์๋๋ก ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ปดํฌ๋ํธ๋ฅผ ์ ๋ฐ์ดํธํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด useLazyLoadQuery๋ฅผ ์ฌ์ฉํ๋ MainContent ์ปดํฌ๋ํธ๊ฐ ์ fetchKey ๋ฐ fetchPolicy๋ก ๋ค์ ๋ ๋๋งํ๊ณ ๋ ๋๋ง ์ ์ฟผ๋ฆฌ๋ฅผ ๋ค์ ๊ฐ์ ธ์ต๋๋ค.
์ ๋ฐ์ดํธ ๋ง๋ค ์ฆ๊ฐํ๋ fetchKey ์ ์๋ก์ด ๊ฐ์ ์ ๋ฌํฉ๋๋ค. ๋ชจ๋ ์ ๋ฐ์ดํธ์์ LazyLoadQuery๋ฅผ ์ฌ์ฉํ ์ fetchKey๋ฅผ ์ ๋ฌํ๋ฉด ์ฟผ๋ฆฌ๊ฐ ์์ ํ ์ฌํ๊ฐ๋๊ณ ๋ค์ ํธ์ถ๋ฉ๋๋ค.
ํญ์ ๋คํธ์ํฌ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๋ก์ปฌ๋ฐ์ดํฐ ์บ์๋ฅผ ๊ฑด๋ ๋ฐ๊ธฐ ์ํด 'network-only' ์ fetchPolicy ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์๋ก ๊ณ ์นจ ์ ์ํ ์ ๋ฐ์ดํธ๋ ์ฌ์ฉ ์ค์ธ fetchPolicy๋ก ์ธํด ํญ์ ๋คํธ์ํฌ ์์ฒญ์ด ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ๊ฐ suspend ๋ฉ๋๋ค. ๋ก๋ฉ์ํ๋ฅผ ํํํ๊ธฐ ์ํด์ Suspense boundary๋ก MainContent ์ปดํฌ๋ํธ๊ฐ ๊ฐ์ธ์ ธ์๋์ง ์ดํด๋ด์ผํฉ๋๋ค.
๋ง์ฝ Suspense ๋ฅผ ํผํด์ผํ๋ค๋ฉด
๊ฒฝ์ฐ์ ๋ฐ๋ผ Suspense ์ fallback ์ ๋ณด์ฌ์ฃผ์ง ์๊ณ ์ถ์ ๋๊ฐ ์์ต๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ์๋ ๋ก๋ฉ์ํ๋ฅผ ์๋์ผ๋ก ์ถ์ ํ ์์๋ fetchQuery ๋ฅผ ๋์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ฐธ๊ณ
๋์ ๋ ๋๋ง์ด ์ง์๋๋ ์ดํ์ React ๋ฒ์ ์์๋ ์ด ๊ฒฝ์ฐ์ ๋ํด์ ์ง์ํ ๊ฒ์ ๋๋ค Suspending ์์ ์ด๋ฏธ ๋ฆฌ๋ ๋๋ง๋ ๋ด์ฉ๋ค์ด Suspense fallback ์ผ๋ก ์จ๊ฒจ์ง๋ ๊ฒ์ ํผํ๋๋ก ํด์ค ๊ฒ์ ๋๋ค.
/**
* App.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const variables = {id: '4'}
const environment = useRelayEnvironment();
const [refreshedQueryOptions, setRefreshedQueryOptions] = useState(null);
const [isRefreshing, setIsRefreshing] = useState(false)
const refresh = useCallback(() => {
if (isRefreshing) { return; }
setIsRefreshing(true);
// fetchQuery will fetch the query and write
// the data to the Relay store. This will ensure
// that when we re-render, the data is already
// cached and we don't suspend
fetchQuery(environment, AppQuery, variables)
.subscribe({
complete: () => {
setIsRefreshing(false);
// *After* the query has been fetched, we update
// our state to re-render with the new fetchKey
// and fetchPolicy.
// At this point the data for the query should
// be cached, so we use the 'store-only'
// fetchPolicy to avoid suspending.
setRefreshedQueryOptions(prev => ({
fetchKey: (prev?.fetchKey ?? 0) + 1,
fetchPolicy: 'store-only',
}));
}
error: () => {
setIsRefreshing(false);
}
});
}, [/* ... */]);
return (
<React.Suspense fallback="Loading query...">
<MainContent
isRefreshing={isRefreshing}
refresh={refresh}
queryOptions={refreshedQueryOptions ?? {}}
variables={variables}
/>
</React.Suspense>
);
}
์ฐ๋ฆฌ๊ฐ ์๋ก๊ณ ์นจํ ๋, ์ด์ ๋ ์ฐ๋ฆฌ์ isRefreshing ๋ก๋ฉ ์ํ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ์ด ์ํ๋ฅผ ์ฌ์ฉํด์ MainContent ๊ตฌ์ฑ ์์ ๋ด์์ MainContent๋ฅผ ์จ๊ธฐ์ง ์๊ณ ์ฌ์ฉ ์ค์ธ ์คํผ๋ ๋๋ ์ ์ฌํ ๋ก๋ฉ UI๋ฅผ ๋ ๋๋ง ํ ์ ์์ต๋๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ๋จผ์ fetchQuery๋ฅผ ํธ์ถํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๋ก์ปฌ ๋ฆด๋ ์ด ์ ์ฅ์์ ๋ฐ์ดํฐ๋ฅผ ์๋๋ค. fetchQuery ๋คํธ์ํฌ ์์ฒญ์ด ์๋ฃ๋๋ฉด ์ํ๋ฅผ ์ ๋ฐ์ดํธํ์ฌ ์ ๋ฐ์ดํธ๋ fetchKey์ fetchPolicy๋ฅผ ๋ค์ ๋ ๋ํ ๋ค์ useLazyLoadQuery๋ก ์ ๋ฌํ์ฌ ์ด์ ์์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ๋ฐ์ดํธ๋ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํฉ๋๋ค.
์ด ์์ ์์ ์ํ๋ฅผ ์ ๋ฐ์ดํธํ ๋ ์ฟผ๋ฆฌ์ ๋ํ ๋ฐ์ดํฐ๊ฐ ๋ก์ปฌ ๋ฆด๋ ์ด ์ ์ฅ์์ ์ด๋ฏธ ์บ์๋์ด์ผ ํ๋ฏ๋ก ์ผ์ ์ค๋จ์ ํผํ๊ณ ์ด๋ฏธ ์บ์๋ ๋ฐ์ดํฐ๋ง ์ฝ๊ธฐ ์ํด 'store-only'์ fetchPolicy๋ฅผ ์ฌ์ฉํฉ๋๋ค.
Refetching Queries with Different Data
"refetching a query" ๋ ๋ค์ ๊ทธ ์ฟผ๋ฆฌ๋ก ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฅผ ์์ฒญ ํ๋ ๊ฒ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ํ์ฌ ์ ํํ ํญ๋ชฉ์ ๋ณ๊ฒฝํ๊ฑฐ๋, ํ์๋๋ ํญ๋ชฉ๊ณผ ๋ค๋ฅธ ํญ๋ชฉ ๋ชฉ๋ก์ ๋ ๋๋งํ๊ฑฐ๋, ์ผ๋ฐ์ ์ผ๋ก ํ์ฌ ๋ ๋๋ง๋ ๋ด์ฉ์ ์ ๋ด์ฉ์ด๋ ๋ค๋ฅธ ๋ด์ฉ์ผ๋ก ๋ณํํ๊ธฐ ์ํ ๊ฒ์ผ ์ ์์ต๋๋ค.
useQueryLoader
/ loadQuery
์ฌ์ฉ
useQueryLoader
/ loadQuery
์ฌ์ฉRefreshing Queries with useQueryLoader
์ ์ ์ฌํ ๋ฐฉ๋ฒ์ผ๋ก refetch ๋ฅผ ํ ์ ์์ต๋๋ค.
/**
* App.react.js
*/
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const variables = {id: '4'};
const [queryRef, loadQuery] = useQueryLoader<AppQueryType>(
AppQuery,
props.appQueryRef /* initial query ref */
);
const refetch = useCallback(() => {
// Load the query again using the same original variables.
// Calling loadQuery will update the value of queryRef.
loadQuery({id: 'different-id'});
}, [/* ... */]);
return (
<React.Suspense fallback="Loading query...">
<MainContent
refetch={refetch}
queryRef={queryRef}
/>
</React.Suspense>
);
}
/**
* MainContent.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
// Renders the preloaded query, given the query reference
function MainContent(props) {
const {refetch, queryRef} = props;
const data = usePreloadedQuery<AppQueryType>(
graphql`
query AppQuery($id: ID!) {
user(id: $id) {
name
friends {
count
}
}
}
`,
queryRef,
);
return (
<>
<h1>{data.user?.name}</h1>
<div>Friends count: {data.user?.friends?.count}</div>
<Button
onClick={() => refetch()}>
Fetch latest count
</Button>
</>
);
}
์ ์์ ๋ฅผ ์ดํด๋ณด๋ฉด,
์ฐ๋ฆฌ๋ loadQuery ํจ์๋ฅผ ์๋ก๊ณ ์นจ์ ์ํ ์ด๋ฒคํธ ํธ๋ค๋ฌ ์์์ ํธ์ถํ๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ request ๋ ์ฆ์ ์์๋๊ณ ์ ๋ฐ์ดํธ๋ queryRef๋ฅผ usePreloaded๋ฅผ ์ฌ์ฉํ๋ MainContent ์ปดํฌ๋ํธ์ ์ ๋ฌํฉ๋๋ค.- ์ ๋ฐ์ดํธ๋ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํฉ๋๋ค.
loadQuery์ fetchPolicy๋ฅผ ์ ๋ฌํ์ง ์๊ณ ์์ต๋๋ค. ์ฆ, 'store-or-network'์ ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉํฉ๋๋ค. ๋ก์ปฌ ์บ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์ง ์ฌ๋ถ๋ฅผ ์ง์ ํ๊ธฐ ์ํด ๋ค๋ฅธ ์ ์ฑ ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
store-or-network : ๋ฐ์ดํฐ๊ฐ ๋ฆด๋ ์ด ์ ์ฅ์์ ์บ์๋์ด์๋์ง ์๋์ง ํ์ธ ํ ์กด์ฌํ์ง ์๋๋ค๋ฉด ๋คํธ์ํฌ ์์ฒญ์ ๋ณด๋ด๋ fetch Policy
๋คํธ์ํฌ ์์ฒญ์ ๋ณด๋ผ ์๋ ์๊ธฐ ๋๋ฌธ์ loadQuery ๋ฅผ ํธ์ถํ๋ ๊ฒ์ ๋ ๋๋ง์ ๋ง๋ usePreloadedQuery ๊ฐ ๋ฐ์ํ ์๋ ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ๋ก๋ฉ์ํ๋ฅผ ํํํ๊ธฐ ์ํด์ Suspense boundary๋ก MainContent ์ปดํฌ๋ํธ๊ฐ ๊ฐ์ธ์ ธ์๋์ง ์ดํด๋ด์ผํฉ๋๋ค.
๋ง์ฝ Suspense ๋ฅผ ํผํด์ผํ๋ค๋ฉด
๊ฒฝ์ฐ์ ๋ฐ๋ผ Suspense ์ fallback ์ ๋ณด์ฌ์ฃผ์ง ์๊ณ ์ถ์ ๋๊ฐ ์์ต๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ์๋ ๋ก๋ฉ์ํ๋ฅผ ์๋์ผ๋ก ์ถ์ ํ ์์๋ fetchQuery ๋ฅผ ๋์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ฐธ๊ณ
๋์ ๋ ๋๋ง์ด ์ง์๋๋ ์ดํ์ React ๋ฒ์ ์์๋ ์ด ๊ฒฝ์ฐ์ ๋ํด์ ์ง์ํ ๊ฒ์ ๋๋ค Suspending ์์ ์ด๋ฏธ ๋ฆฌ๋ ๋๋ง๋ ๋ด์ฉ๋ค์ด Suspense fallback ์ผ๋ก ์จ๊ฒจ์ง๋ ๊ฒ์ ํผํ๋๋ก ํด์ค ๊ฒ์ ๋๋ค.
/**
* App.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const environment = useRelayEnvironment();
const [queryRef, loadQuery] = useQueryLoader<AppQueryType>(
AppQuery,
props.appQueryRef /* initial query ref */
);
const [isRefetching, setIsRefetching] = useState(false)
const refetch = useCallback(() => {
if (isRefetching) { return; }
setIsRefetching(true);
// fetchQuery will fetch the query and write
// the data to the Relay store. This will ensure
// that when we re-render, the data is already
// cached and we don't suspend
fetchQuery(environment, AppQuery, variables)
.subscribe({
complete: () => {
setIsRefetching(false);
// *After* the query has been fetched, we call
// loadQuery again to re-render with a new
// queryRef.
// At this point the data for the query should
// be cached, so we use the 'store-only'
// fetchPolicy to avoid suspending.
loadQuery({id: 'different-id'}, {fetchPolicy: 'store-only'});
}
error: () => {
setIsRefetching(false);
}
});
}, [/* ... */]);
return (
<React.Suspense fallback="Loading query...">
<MainContent
isRefetching={isRefetching}
refetch={refetch}
queryRef={queryRef}
/>
</React.Suspense>
);
}
์ฐ๋ฆฌ๊ฐ ์๋ก๊ณ ์นจํ ๋, ์ด์ ๋ ์ฐ๋ฆฌ์ isRefreshing ๋ก๋ฉ ์ํ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ์ด ์ํ๋ฅผ ์ฌ์ฉํด์ MainContent ๊ตฌ์ฑ ์์ ๋ด์์ MainContent๋ฅผ ์จ๊ธฐ์ง ์๊ณ ์ฌ์ฉ ์ค์ธ ์คํผ๋ ๋๋ ์ ์ฌํ ๋ก๋ฉ UI๋ฅผ ๋ ๋๋ง ํ ์ ์์ต๋๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ๋จผ์ fetchQuery๋ฅผ ํธ์ถํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๋ก์ปฌ ๋ฆด๋ ์ด ์ ์ฅ์์ ๋ฐ์ดํฐ๋ฅผ ์๋๋ค. fetchQuery ๋คํธ์ํฌ ์์ฒญ์ด ์๋ฃ๋๋ฉด loadQuery๋ฅผ ํธ์ถํ์ฌ ์ ๋ฐ์ดํธ๋ queryRef๋ฅผ ์ป์ ๋ค์ usePreloaded๋ก ์ ๋ฌํฉ๋๋ค.์ฟผ๋ฆฌ๋ ์ด์ ์์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ๋ฐ์ดํธ๋ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํฉ๋๋ค.
์ด๋ loadQuery๊ฐ ํธ์ถ๋๋ฉด ์ฟผ๋ฆฌ์ ๋ํ ๋ฐ์ดํฐ๊ฐ ๋ก์ปฌ ๋ฆด๋ ์ด ์ ์ฅ์์ ์ด๋ฏธ ์บ์๋์ด ์์ด์ผ ํ๋ฏ๋ก ์ผ์ ์ค๋จ์ ๋ฐฉ์งํ๊ณ ์ด๋ฏธ ์บ์๋ ๋ฐ์ดํฐ๋ง ์ฝ๊ธฐ ์ํด 'store-only'์ fetchPolicy๋ฅผ ์ฌ์ฉํฉ๋๋ค.
useLazyLoadQuery
์ฌ์ฉ
useLazyLoadQuery
์ฌ์ฉRefreshing Queries with useLazyLoadQuery
์ ์ ์ฌํ ๋ฐฉ๋ฒ์ผ๋ก refetch ๋ฅผ ํ ์ ์์ต๋๋ค.
/**
* App.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const [queryArgs, setQueryArgs] = useState({
options: {fetchKey: 0},
variables: {id: '4'},
});
const refetch = useCallback(() => {
// Trigger a re-render of useLazyLoadQuery with new variables,
// *and* an updated fetchKey.
// The new fetchKey will ensure that the query is fully
// re-evaluated and refetched.
setQueryArgs(prev => ({
options: {
fetchKey: (prev?.options.fetchKey ?? 0) + 1,
},
variables: {id: 'different-id'}
}));
}, [/* ... */]);
return (
<React.Suspense fallback="Loading query...">
<MainContent
refetch={refetch}
queryArgs={queryArgs}
/>
</React.Suspense>
);
}
/**
* MainContent.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
// Fetches and renders the query, given the fetch options
function MainContent(props) {
const {refetch, queryArgs} = props;
const data = useLazyLoadQuery<AppQueryType>(
graphql`
query AppQuery($id: ID!) {
user(id: $id) {
name
friends {
count
}
}
}
`,
queryArgs.variables,
queryArgs.options,
);
return (
<>
<h1>{data.user?.name}</h1>
<div>Friends count: {data.user.friends?.count}</div>
<Button
onClick={() => refetch()}>
Fetch latest count
</Button>
</>
);
}
์ํ์์์ ์ ์ต์ ์ ์ค์ ํ์ฌ ์๋ก ๊ณ ์น ์ ์๋๋ก ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ปดํฌ๋ํธ๋ฅผ ์ ๋ฐ์ดํธํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด useLazyLoadQuery๋ฅผ ์ฌ์ฉํ๋ MainContent ์ปดํฌ๋ํธ๊ฐ ์ ๋ณ์์ fetchKey๋ก ๋ค์ ๋ ๋๋ฅผ ์ํํ๊ณ ๋ ๋๋ง ์ ์ฟผ๋ฆฌ๋ฅผ ๋ค์ ๊ฐ์ ธ์ต๋๋ค.
์ ๋ฐ์ดํธ ๋ง๋ค ์ฆ๊ฐํ๋ fetchKey ์ ์๋ก์ด ๊ฐ์ ์ ๋ฌํฉ๋๋ค. ๋ชจ๋ ์ ๋ฐ์ดํธ์์ LazyLoadQuery๋ฅผ ์ฌ์ฉํ ์ fetchKey๋ฅผ ์ ๋ฌํ๋ฉด ์ฟผ๋ฆฌ๊ฐ ์์ ํ ์ฌํ๊ฐ๋๊ณ ๋ค์ ํธ์ถ๋ฉ๋๋ค.
LazyLoadQuery๋ฅผ ์ฌ์ฉํ fetchPolicy๋ฅผ ์ ๋ฌํ์ง ์์ต๋๋ค. ์ฆ, 'store-or-network'์ ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉํฉ๋๋ค.
์๋ก ๊ณ ์นจ ์ ์ํ ์ ๋ฐ์ดํธ๋ ์ฌ์ฉ ์ค์ธ fetchPolicy๋ก ์ธํด ํญ์ ๋คํธ์ํฌ ์์ฒญ์ด ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ๊ฐ suspend ๋ฉ๋๋ค. ๋ก๋ฉ์ํ๋ฅผ ํํํ๊ธฐ ์ํด์ Suspense boundary๋ก MainContent ์ปดํฌ๋ํธ๊ฐ ๊ฐ์ธ์ ธ์๋์ง ์ดํด๋ด์ผํฉ๋๋ค.
๋ง์ฝ Suspense ๋ฅผ ํผํด์ผํ๋ค๋ฉด
๊ฒฝ์ฐ์ ๋ฐ๋ผ Suspense ์ fallback ์ ๋ณด์ฌ์ฃผ์ง ์๊ณ ์ถ์ ๋๊ฐ ์์ต๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ์๋ ๋ก๋ฉ์ํ๋ฅผ ์๋์ผ๋ก ์ถ์ ํ ์์๋ fetchQuery ๋ฅผ ๋์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ฐธ๊ณ
๋์ ๋ ๋๋ง์ด ์ง์๋๋ ์ดํ์ React ๋ฒ์ ์์๋ ์ด ๊ฒฝ์ฐ์ ๋ํด์ ์ง์ํ ๊ฒ์ ๋๋ค Suspending ์์ ์ด๋ฏธ ๋ฆฌ๋ ๋๋ง๋ ๋ด์ฉ๋ค์ด Suspense fallback ์ผ๋ก ์จ๊ฒจ์ง๋ ๊ฒ์ ํผํ๋๋ก ํด์ค ๊ฒ์ ๋๋ค.
/**
* App.react.js
*/
import type {AppQuery as AppQueryType} from 'AppQuery.graphql';
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const environment = useRelayEnvironment();
const [isRefreshing, setIsRefreshing] = useState(false)
const [queryArgs, setQueryArgs] = useState({
options: {fetchKey: 0, fetchPolicy: 'store-or-network'},
variables: {id: '4'},
});
const refetch = useCallback(() => {
if (isRefreshing) { return; }
setIsRefreshing(true);
// fetchQuery will fetch the query and write
// the data to the Relay store. This will ensure
// that when we re-render, the data is already
// cached and we don't suspend
fetchQuery(environment, AppQuery, variables)
.subscribe({
complete: () => {
setIsRefreshing(false);
// *After* the query has been fetched, we update
// our state to re-render with the new fetchKey
// and fetchPolicy.
// At this point the data for the query should
// be cached, so we use the 'store-only'
// fetchPolicy to avoid suspending.
setQueryArgs(prev => ({
options: {
fetchKey: (prev?.options.fetchKey ?? 0) + 1,
fetchPolicy: 'store-only',
},
variables: {id: 'different-id'}
}));
},
error: () => {
setIsRefreshing(false);
}
});
}, [/* ... */]);
return (
<React.Suspense fallback="Loading query...">
<MainContent
isRefetching={isRefetching}
refetch={refetch}
queryArgs={queryArgs}
/>
</React.Suspense>
);
}
์ฐ๋ฆฌ๊ฐ ์๋ก๊ณ ์นจํ ๋, ์ด์ ๋ ์ฐ๋ฆฌ์ isRefreshing ๋ก๋ฉ ์ํ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ์ด ์ํ๋ฅผ ์ฌ์ฉํด์ MainContent ๊ตฌ์ฑ ์์ ๋ด์์ MainContent๋ฅผ ์จ๊ธฐ์ง ์๊ณ ์ฌ์ฉ ์ค์ธ ์คํผ๋ ๋๋ ์ ์ฌํ ๋ก๋ฉ UI๋ฅผ ๋ ๋๋ง ํ ์ ์์ต๋๋ค.
์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ๋จผ์ fetchQuery๋ฅผ ํธ์ถํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ค๊ณ ๋ก์ปฌ ๋ฆด๋ ์ด ์ ์ฅ์์ ๋ฐ์ดํฐ๋ฅผ ์๋๋ค. fetchQuery ๋คํธ์ํฌ ์์ฒญ์ด ์๋ฃ๋๋ฉด ์ํ๋ฅผ ์ ๋ฐ์ดํธํ์ฌ ์ ๋ฐ์ดํธ๋ fetchKey์ fetchPolicy๋ฅผ ๋ค์ ๋ ๋ํ ๋ค์ useLazyLoadQuery๋ก ์ ๋ฌํ์ฌ ์ด์ ์์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ๋ฐ์ดํธ๋ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํฉ๋๋ค.
์ด ์์ ์์ ์ํ๋ฅผ ์ ๋ฐ์ดํธํ ๋ ์ฟผ๋ฆฌ์ ๋ํ ๋ฐ์ดํฐ๊ฐ ๋ก์ปฌ ๋ฆด๋ ์ด ์ ์ฅ์์ ์ด๋ฏธ ์บ์๋์ด์ผ ํ๋ฏ๋ก ์ผ์ ์ค๋จ์ ํผํ๊ณ ์ด๋ฏธ ์บ์๋ ๋ฐ์ดํฐ๋ง ์ฝ๊ธฐ ์ํด 'store-only'์ fetchPolicy๋ฅผ ์ฌ์ฉํฉ๋๋ค.
Last updated