刷新查询
当提到 **“刷新查询”** 时,我们的意思是获取最初由查询渲染的**完全相同的数据**,以便从服务器获取该数据的最新版本。
使用实时功能
如果我们希望让我们的数据与服务器上的最新版本保持同步,首先要考虑的是是否适合使用任何实时功能,这些功能可以更轻松地自动使数据保持最新,而无需定期手动刷新数据。其中一个示例是使用 GraphQL 订阅,这将需要在您的服务器上进行额外的配置,以及在 网络层 上进行配置。
使用 useQueryLoader
/ loadQuery
要使用我们useQueryLoader
Hook (在我们的 获取查询以进行渲染 部分中描述)刷新查询,我们只需要再次调用 loadQuery
。
/**
* App.react.js
*/
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const [queryRef, loadQuery] = useQueryLoader(
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
*/
// Renders the preloaded query, given the query reference
function MainContent(props) {
const {refresh, queryRef} = props;
const data = usePreloadedQuery(
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
,因此网络请求会立即开始,然后将更新后的queryRef
传递给使用usePreloadedQuery
的MainContent
组件,以便它渲染更新后的数据。 - 我们传递了
fetchPolicy
为'network-only'
,以确保我们始终从网络获取数据并跳过本地数据缓存。 - 调用
loadQuery
会重新渲染组件并导致usePreloadedQuery
挂起(如 使用 Suspense 的加载状态 中所述),因为由于我们使用的fetchPolicy
,网络请求将始终被发出。这意味着我们需要确保在MainContent
组件周围有一个Suspense
边界,以便显示回退加载状态。
如果您需要避免 Suspense
在某些情况下,您可能希望避免显示 Suspense 回退,这会隐藏已渲染的内容。对于这些情况,您可以使用 fetchQuery
而不是,并手动跟踪加载状态。
注意
在 React 的未来版本中,当支持并发渲染时,React 将提供一个选项来支持这种情况,并在挂起时避免使用 Suspense 回退隐藏已渲染的内容。
/**
* App.react.js
*/
const AppQuery = require('__generated__/AppQuery.graphql');
function App(props: Props) {
const environment = useRelayEnvironment();
const [queryRef, loadQuery] = useQueryLoader(
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
组件内渲染繁忙的微调器或类似的加载 UI,而**不会**隐藏MainContent
。 - 在事件处理程序中,我们首先调用
fetchQuery
,它将获取查询并将数据写入本地 Relay 存储。当fetchQuery
网络请求完成时,我们调用loadQuery
,以便获得更新后的queryRef
,然后将其传递给usePreloadedQuery
以便渲染更新后的数据,类似于前面的示例。 - 此时,当调用
loadQuery
时,查询的数据应该已经在本地 Relay 存储中缓存,因此我们使用fetchPolicy
为'store-only'
来避免挂起,并且只读取已缓存的数据。
使用 useLazyLoadQuery
要使用我们useLazyLoadQuery
Hook (在我们的 延迟获取查询以进行渲染 部分中描述)刷新查询,我们可以执行以下操作。
/**
* App.react.js
*/
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
*/
// Fetches and renders the query, given the fetch options
function MainContent(props) {
const {refresh, queryOptions, variables} = props;
const data = useLazyLoadQuery(
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
值,我们会在每次更新时递增它。在每次更新时将新的fetchKey
传递给useLazyLoadQuery
将确保查询被完全重新评估并重新获取。 - 我们传递了
fetchPolicy
为'network-only'
,以确保我们始终从网络获取数据并跳过本地数据缓存。 refresh
中的状态更新将导致组件挂起(如 使用 Suspense 的加载状态 中所述),因为由于我们使用的fetchPolicy
,网络请求将始终被发出。这意味着我们需要确保在MainContent
组件周围有一个Suspense
边界,以便显示回退加载状态。
如果您需要避免 Suspense
在某些情况下,您可能希望避免显示 Suspense 回退,这会隐藏已渲染的内容。对于这些情况,您可以使用 fetchQuery
而不是,并手动跟踪加载状态。
注意
在 React 的未来版本中,当支持并发渲染时,React 将提供一个选项来支持这种情况,并在挂起时避免使用 Suspense 回退隐藏已渲染的内容。
/**
* 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
组件内渲染繁忙的微调器或类似的加载 UI,而**不会**隐藏MainContent
。 - 在事件处理程序中,我们首先调用
fetchQuery
,它将获取查询并将数据写入本地 Relay 存储。当fetchQuery
网络请求完成时,我们更新我们的状态,以便我们重新渲染一个更新的fetchKey
和fetchPolicy
,然后将其传递给useLazyLoadQuery
以便渲染更新后的数据,类似于前面的示例。 - 此时,当我们更新状态时,查询的数据应该已经在本地 Relay 存储中缓存,因此我们使用
fetchPolicy
为'store-only'
来避免挂起,并且只读取已缓存的数据。
此页面是否有用?
通过以下方式帮助我们改进网站: 回答几个快速问题.