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

使用不同数据重新获取查询

当提到“重新获取查询”时,我们的意思是再次获取查询以获取与最初由查询呈现的不同数据。例如,这可能是为了更改当前选择的项目,渲染与正在显示的列表不同的项目列表,或者更一般地将当前呈现的内容转换为显示新的或不同的内容。

使用 useQueryLoader / loadQuery

使用 useQueryLoader 刷新查询 类似,我们也可以使用我们 渲染查询获取 部分中描述的 useQueryLoader Hook,但这次传入不同的查询变量

/**
* App.react.js
*/
const AppQuery = require('__generated__/AppQuery.graphql');

function App(props: Props) {
const variables = {id: '4'};
const [queryRef, loadQuery] = useQueryLoader(
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
*/

// Renders the preloaded query, given the query reference
function MainContent(props) {
const {refetch, 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={() => refetch()}>
Fetch latest count
</Button>
</>
);
}

让我们分析一下这里发生了什么

  • 我们在重新获取的事件处理程序中调用 loadQuery,因此网络请求会立即开始,然后将 queryRef 传递给 usePreloadedQuery,以便它呈现更新后的数据。
  • 我们没有向 loadQuery 传递 fetchPolicy,这意味着它将使用默认值 'store-or-network'。我们可以提供不同的策略来指定是否使用本地缓存数据(正如我们在 重新使用缓存数据进行渲染 中所述)。
  • 调用 loadQuery 将重新渲染组件,并可能导致 usePreloadedQuery 挂起(如 使用 Suspense 的加载状态 中所述)。这意味着我们需要确保有一个 Suspense 边界包装 MainContent 组件,以便显示备用加载状态。

如果你需要避免 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 [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>
);
}

让我们分析一下这里发生了什么

  • 在重新获取时,我们现在会跟踪我们自己的 isRefetching 加载状态,因为我们正在避免挂起。我们可以使用此状态在 MainContent 组件内渲染一个忙碌的微调器或类似的加载 UI,而不会隐藏 MainContent
  • 在事件处理程序中,我们首先调用 fetchQuery,它将获取查询并将数据写入本地 Relay 存储。当 fetchQuery 网络请求完成后,我们调用 loadQuery,以便获得一个更新的 queryRef,然后将其传递给 usePreloadedQuery 以渲染更新后的数据,类似于之前的示例。
  • 此时,当调用 loadQuery 时,查询的数据应该已经被缓存到本地 Relay 存储中,因此我们使用 fetchPolicy'store-only' 来避免挂起,并且只读取已经缓存的数据。

使用 useLazyLoadQuery

使用 useLazyLoadQuery 刷新查询 类似,我们也可以使用我们 useLazyLoadQuery 部分中描述的 渲染期间延迟获取查询 Hook,但这次传入不同的查询变量

/**
* App.react.js
*/
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
*/
// Fetches and renders the query, given the fetch options
function MainContent(props) {
const {refetch, queryArgs} = props;
const data = useLazyLoadQuery(
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>
</>
);
}

让我们分析一下这里发生了什么

  • 我们在重新获取的事件处理程序中更新组件,方法是在状态中设置新的查询参数。这将导致使用 useLazyLoadQueryMainContent 组件使用新的 variablesfetchKey 重新渲染,并在渲染时重新获取查询。
  • 我们传递了一个新的 fetchKey 值,我们在每次更新时都会递增它。在每次更新时向 useLazyLoadQuery 传递一个新的 fetchKey 将确保查询被完全重新评估和重新获取。
  • 我们没有向 useLazyLoadQuery 传递新的 fetchPolicy,这意味着它将使用默认值 'store-or-network'。我们可以提供不同的策略来指定是否使用本地缓存数据(正如我们在 重新使用缓存数据进行渲染 中所述)。
  • refetch 中的状态更新将重新渲染组件,并可能导致组件挂起(如 使用 Suspense 的加载状态 中所述)。这意味着我们需要确保有一个 Suspense 边界包装 MainContent 组件,以便显示备用加载状态。

如果你需要避免 Suspense

在某些情况下,你可能希望避免显示 Suspense 备用,这将隐藏已经渲染的内容。对于这些情况,你可以使用 fetchQuery 而不是,并手动跟踪加载状态

注意

在 React 的未来版本中,当支持并发渲染时,React 将提供一个选项来支持这种情况,并在挂起时避免使用 Suspense 备用隐藏已经渲染的内容。

/**
* App.react.js
*/
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>
);
}

让我们分析一下这里发生了什么

  • 在重新获取时,我们现在会跟踪我们自己的 isRefetching 加载状态,因为我们正在避免挂起。我们可以使用此状态在 MainContent 组件内渲染一个忙碌的微调器或类似的加载 UI,而不会隐藏 MainContent
  • 在事件处理程序中,我们首先调用 fetchQuery,它将获取查询并将数据写入本地 Relay 存储。当 fetchQuery 网络请求完成后,我们更新状态,以便我们重新渲染一个更新的 fetchKeyfetchPolicy,然后将其传递给 useLazyLoadQuery 以渲染更新后的数据,类似于之前的示例。
  • 此时,当我们更新状态时,查询的数据应该已经被缓存到本地 Relay 存储中,因此我们使用 fetchPolicy'store-only' 来避免挂起,并且只读取已经缓存的数据。

此页面是否有用?

请帮助我们通过以下方式使网站变得更好 回答一些简短的问题.