跳至主要内容
版本:v18.0.0

刷新查询

当提到 **“刷新查询”** 时,我们的意思是获取最初由查询渲染的**完全相同的数据**,以便从服务器获取该数据的最新版本。

使用实时功能

如果我们希望让我们的数据与服务器上的最新版本保持同步,首先要考虑的是是否适合使用任何实时功能,这些功能可以更轻松地自动使数据保持最新,而无需定期手动刷新数据。

其中一个示例是使用 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 传递给使用 usePreloadedQueryMainContent 组件,以便它渲染更新后的数据。
  • 我们传递了 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>
</>
);
}

让我们提炼一下这里发生的事情。

  • 我们在刷新事件处理程序中更新组件,在状态中设置新的选项。这将导致使用 useLazyLoadQueryMainContent 组件使用新的 fetchKeyfetchPolicy 重新渲染,并在渲染时重新获取查询。
  • 我们传递了一个新的 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 网络请求完成时,我们更新我们的状态,以便我们重新渲染一个更新的 fetchKeyfetchPolicy,然后将其传递给 useLazyLoadQuery 以便渲染更新后的数据,类似于前面的示例。
  • 此时,当我们更新状态时,查询的数据应该已经在本地 Relay 存储中缓存,因此我们使用 fetchPolicy'store-only' 来避免挂起,并且只读取已缓存的数据。

此页面是否有用?

通过以下方式帮助我们改进网站: 回答几个快速问题.