news 2026/9/23 4:45:37 Relay 17 连接(Connection)重取与筛选:使用 usePaginationFragment 的 refetch 实现过滤与排序 张小明 前端开发工程师 1.2k 24 Relay 17 连接(Connection)重取与筛选:使用 usePaginationFragment 的 refetch 实现过滤与排序 前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址:https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载Relay 17 连接(Connection)重取与筛选:使用 usePaginationFragment 的 refetch 实现过滤与排序导读:本指南聚焦 Relay 中“带筛选条件的连接数据重取(refetching connections)”这一核心场景。当你的列表需要根据搜索词、排序方式等参数改变查询结果时,如何既能保留分页能力、又能在参数变化时重新获取数据?本文以usePaginationFragment的refetch函数为主线,讲解筛选参数的传递、分页时参数的保留机制、refetch 的触发时机与变量合并规则,并结合react-relay与relay-runtime的源码(usePaginationFragment.js、useRefetchableFragmentInternal.js)揭示底层实现。读完你将掌握在 Relay 17 中实现搜索型列表、排序切换等实战方案。在 GraphQL 中,连接(Connection)字段往往不只是返回一个固定列表,而是可以接收参数,用来筛选(filter)结果集或改变排序方式(sort)。典型场景包括:搜索 typeahead(自动补全):结果列表由用户输入的搜索词动态过滤;切换评论排序模式:对某篇文章的评论,用户可选择不同的排序方式,服务器返回完全不同的评论集合;改变信息流(News Feed)的排序方式。本文要解决的问题正是:当这些“筛选参数”变化时,如何让已渲染的连接数据重新获取(refetch),同时不影响已有的分页(pagination)能力。本文基于仓库中 refetching-connections.md 编写,并结合当前仓库中 Relay 17 版本的源码进行深入验证。一、连接字段如何接收筛选参数在 GraphQL 层面,连接字段可以接收任意参数来筛选或排序结果。例如,下面的 fragment 请求了一个按order_by排序、按search_term过滤的好友列表:fragment UserFragment on User { name friends(order_by: DATE_ADDED, search_term: "Alice", first: 10) { edges { node { name age } } } }其中order_by、search_term是筛选/排序参数,而first是分页参数(表示取前 10 条)。first、after、before、last这类分页参数由 Relay 自动管理,但筛选参数则由业务逻辑控制。在 Relay 中,我们通常不把筛选参数写死成字面量,而是通过 GraphQL 变量(variables)传入,这样运行时才能动态改变它们。使用usePaginationFragment时,fragment 中可以这样写:type Props = { userRef: FriendsListComponent_user$key, }; function FriendsListComponent(props: Props) { const userRef = props.userRef; const {data, ...} = usePaginationFragment( graphql` fragment FriendsListComponent_user on User { name friends( order_by: $orderBy, search_term: $searchTerm, after: $cursor, first: $count, ) @connection(key: "FriendsListComponent_user_friends_connection") { edges { node { name age } } } } `, userRef, ); return (...); }这里有几个关键点:$orderBy、$searchTerm是筛选变量,$cursor、$count是分页变量;@connection(key: "FriendsListComponent_user_friends_connection")指令告诉 Relay 这是一个需要规范化的连接字段,key用于在 Relay store 中唯一标识该连接(详见 transform_connections.rs 中build_connection_metadata等实现);使用usePaginationFragment的前提是 fragment 同时被标注@refetchable(queryName: "..."),编译器会为该 fragment 自动生成一个分页查询(可参考 pagination.md 中的完整写法)。二、分页时筛选参数会被保留usePaginationFragment返回的loadNext(count)用于加载下一页。一个容易忽视但至关重要的行为是:调用loadNext时,会继续使用初始查询时的筛选参数值。type Props = { userRef: FriendsListComponent_user$key, }; function FriendsListComponent(props: Props) { const {data, loadNext} = usePaginationFragment( graphql` fragment FriendsListComponent_user on User { name friends(order_by: $orderBy, search_term: $searchTerm) @connection(key: "FriendsListComponent_user_friends_connection") { edges { node { name age } } } } `, userRef, ); return ( <> <h1>Friends of {data.name}:</h1> <List items={data.friends?.nodes}>{...}</List> {/* Loading the next items will use the original order_by and search_term values used for the initial query */} <Button onClick={() => loadNext(10)}>Load more friends</Button> </> ); }注意:调用loadNext会沿用初始查询时的order_by和search_term值。在分页过程中,这些值不会(也不应该)改变——因为后续页面必须与第一页处于同一筛选条件下,分页才有意义。从源码看,这一行为由 useLoadMoreFunction.js 中的loadMore回调实现:它从 fragment selector 中取出parentVariables和fragmentVariables合并为baseVariables,再调用getPaginationVariables生成分页请求的变量:const parentVariables = fragmentSelector.owner.variables; const fragmentVariables = fragmentSelector.variables; const extraVariables = options?.UNSTABLE_extraVariables; const baseVariables = { ...parentVariables, ...fragmentVariables, }; const paginationVariables = getPaginationVariables( direction, count, cursor, baseVariables, {...extraVariables}, paginationMetadata, );而 getPaginationVariables.js 只会覆盖cursor和count这两个分页变量,其余变量(包括orderBy、searchTerm)原样保留在baseVariables中,从而实现“分页沿用原筛选条件”的效果。同时它会将反向分页的游标/数量变量置为null,确保 forward/backward 两种方向互不干扰:const paginationVariables = { ...baseVariables, ...extraVariables, [forwardMetadata.cursor]: cursor, [forwardMetadata.count]: count, }; if (backwardMetadata && backwardMetadata.cursor) { paginationVariables[backwardMetadata.cursor] = null; } if (backwardMetadata && backwardMetadata.count) { paginationVariables[backwardMetadata.count] = null; }需要临时在分页请求上附加额外变量时,可以使用loadNext(count, {UNSTABLE_extraVariables: {...}}),但注意不能把cursor、count放进extraVariables——源码中对此有显式warning(见 getPaginationVariables.js)。三、用 refetch 以不同的参数重新获取连接如果我们需要用不同的变量重新获取连接——比如用户改变了搜索词或排序方式——就不能再用loadNext,而应使用usePaginationFragment提供的refetch函数。其用法与 Refetching Fragments with Different Data 一节中介绍的useRefetchableFragment的refetch完全一致。3.1 完整示例:搜索词变化时自动重取/** * FriendsListComponent.react.js */ import type {FriendsListComponent_user$key} from 'FriendsListComponent_user.graphql'; const React = require('React'); const {useState, useEffect} = require('React'); const {graphql, usePaginationFragment} = require('react-relay'); type Props = { searchTerm?: string, user: FriendsListComponent_user$key, }; function FriendsListComponent(props: Props) { const searchTerm = props.searchTerm; const {data, loadNext, refetch} = usePaginationFragment( graphql` fragment FriendsListComponent_user on User { name friends( order_by: $orderBy, search_term: $searchTerm, after: $cursor, first: $count, ) @connection(key: "FriendsListComponent_user_friends_connection") { edges { node { name age } } } } `, props.user, ); useEffect(() => { // When the searchTerm provided via props changes, refetch the connection // with the new searchTerm refetch({first: 10, search_term: searchTerm}, {fetchPolicy: 'store-or-network'}); }, [searchTerm]) return ( <> <h1>Friends of {data.name}:</h1> {/* When the button is clicked, refetch the connection but sorted differently */} <Button onClick={() => refetch({first: 10, orderBy: 'DATE_ADDED'}); }> Sort by date added </Button> <List items={data.friends?.nodes}>...</List> <Button onClick={() => loadNext(10)}>Load more friends</Button> </> ); }3.2 提炼关键行为逐条解读上面这个例子:refetch接受新变量集合并重新抓取 fragment。你传入的变量是生成的查询所期望变量的一个子集:生成的查询需要id(当 fragment 类型带有id字段时),以及 fragment 中传递引用的所有其他变量。在本例中,我们必须传first(要抓取的条数),并可传入不同的筛选值如orderBy、searchTerm。注意这里refetch({first: 10, order_by: searchTerm})的写法来自文档示例;实际项目中变量名以你在 fragment 中声明的$orderBy/$searchTerm为准。可能触发 Suspense:refetch会重新渲染组件,如果需要发起并等待网络请求,组件可能进入 suspend 状态(详见 Loading States with Suspense)。因此必须确保组件上层有Suspense边界来展示 fallback。refetch 是“从零开始”:调用refetch在概念上等于从头重新抓取连接,并重置分页状态。例如用不同的search_term抓取后,之前search_term的分页信息不再有意义——我们实际上是在对一个全新列表进行分页。3.3 fetchPolicy 的选择示例中使用了fetchPolicy: 'store-or-network'。Relay 17 中合法的fetchPolicy取值在 RelayRuntimeTypes.js 中定义为:fetchPolicy行为'store-or-network'若 store 中已有满足条件的数据则直接复用(跳过网络请求),否则发起网络请求'store-and-network'先读 store 数据立即渲染,同时发起网络请求,请求完成后用最新数据更新'network-only'始终发起网络请求,不读缓存'store-only'只读 store,绝不发起网络请求若希望避免 Suspense:可先用fetchQuery把数据写入 store,再以fetchPolicy: 'store-only'调用refetch,此时数据已缓存、不会 suspend(完整做法见 refetching-fragments-with-different-data.md 的 "If you need to avoid Suspense" 小节)。refetch还支持UNSTABLE_renderPolicy: 'full' | 'partial'(对应 RelayRuntimeTypes.js 的RenderPolicy)与onComplete回调,类型定义见 useRefetchableFragmentInternal.js。四、源码视角:refetch 与分页是如何协同的usePaginationFragment本质上是useRefetchableFragmentInternal(负责refetch)与useLoadMoreFunction(负责loadNext/loadPrevious)的组合。查看 usePaginationFragment.js 可以看到:通过getPaginationMetadata从 fragment 中解析连接路径与分页元数据;通过useRefetchableFragmentInternal获得refetch能力;分别构造 forward / backward 两个方向的loadNext/loadPrevious;关键点:refetchPagination在真正调用refetch之前,会先disposeFetchNext()和disposeFetchPrevious()——即取消所有在途的分页请求:const refetchPagination = useCallback( (variables: TVariables, options: void | Options) => { disposeFetchNext(); disposeFetchPrevious(); return refetch(variables, {...options, __environment: undefined}); }, [disposeFetchNext, disposeFetchPrevious, refetch], );这从源码层面印证了文档中的结论:refetch 会重置分页状态——旧的分页请求被主动作废,连接从第一条开始重新获取。再看 useRefetchableFragmentInternal.js 中refetch的实现,变量合并顺序为:const refetchVariables: VariablesOf<TQuery> = { ...(parentVariables as $FlowFixMe), ...fragmentVariables, ...providedRefetchVariables, };即:未在refetch调用中提供的变量,会从原 fragment owner 的变量中继承。因此你不需要每次把所有变量都传齐,只需要传“发生变化的”以及必要的first(count)即可。同时,如果生成的查询需要标识符(如id)且未显式传入,Relay 会从 fragment data 中读取并自动填充:if ( identifierInfo != null && !providedRefetchVariables.hasOwnProperty( identifierInfo.identifierQueryVariableName, ) ) { refetchVariables[identifierInfo.identifierQueryVariableName] = identifierValue; }相关运行时期类型与getRefetchMetadata、getPaginationMetadata的返回结构可参考 getPaginationMetadata.js 与relay-runtime的 index.d.ts。@connection指令在编译器侧的处理(连接元数据构建、handle field 生成)位于 transform_connections.rs。五、实践清单与常见陷阱综合文档与源码,给出可复制的实践要点:fragment 需要@refetchable:usePaginationFragment要求 fragment 可 refetch(作用于Viewer、Query、实现Node的类型或@fetchable类型),并配合@connection(key: "...")标注连接字段。筛选变量通过 GraphQL 变量传入:不要把筛选条件写死成字面量,否则无法在运行时改变它们。分页沿用原筛选条件:loadNext/loadPrevious会复用初始查询的筛选变量,不要在分页时试图改变筛选条件。改变筛选条件用refetch:传入新变量(必要时含first),Relay 会自动继承未变化的变量、填充id,并取消在途分页请求、重置分页游标。务必有Suspense边界:refetch可能触发 suspend,组件上层需要Suspense包裹;想避免 Suspense 时使用fetchQuery+fetchPolicy: 'store-only'的组合。变量命名一致性:GraphQL 变量名($orderBy/$searchTerm/$cursor/$count)必须在 fragment 中声明并在查询根(@refetchable生成查询)中可用;生成的查询会要求id(若类型有id字段)以及 fragment 中传递引用的所有其他变量。延伸阅读分页入门与loadNext/hasNext用法:pagination.md连接(Connection)概念与游标分页:connections.md通用 fragment 重取(useRefetchableFragment):refetching-fragments-with-different-data.md高级分页(自定义 append/prepend 行为):advanced-pagination.md运行时实现:usePaginationFragment.js、useRefetchableFragmentInternal.js、getPaginationVariables.js赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址:https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐OpenHermes-2.5-neural-chat-7b-v3-1-7B深度解析:Mistral架构融合模型如何革新AI对话体验OpenHermes 2.5 neural chat 7b v3 1 7B深度解析:Mistral架构融合模型如何革新AI对话体验 想要体验 Mistral架构前端开发工具Relay 连接(Connection)重取指南:使用与变更筛选条件(usePaginationFragment 的 refetch 实战解析)Relay 连接(Connection)重取指南:使用与变更筛选条件(usePaginationFragment 的 refetch 实战解析) 在 Relay前端开发工具Relay 连接数据重新获取(Refetching Connections):使用 usePaginationFragment 实现过滤与排序变量切换Relay 连接数据重新获取(Refetching Connections):使用 usePaginationFragment 实现过滤与排序变量切换 导读 在前端开发工具
前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址:https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载Relay 17 连接(Connection)重取与筛选:使用 usePaginationFragment 的 refetch 实现过滤与排序导读:本指南聚焦 Relay 中“带筛选条件的连接数据重取(refetching connections)”这一核心场景。当你的列表需要根据搜索词、排序方式等参数改变查询结果时,如何既能保留分页能力、又能在参数变化时重新获取数据?本文以usePaginationFragment的refetch函数为主线,讲解筛选参数的传递、分页时参数的保留机制、refetch 的触发时机与变量合并规则,并结合react-relay与relay-runtime的源码(usePaginationFragment.js、useRefetchableFragmentInternal.js)揭示底层实现。读完你将掌握在 Relay 17 中实现搜索型列表、排序切换等实战方案。在 GraphQL 中,连接(Connection)字段往往不只是返回一个固定列表,而是可以接收参数,用来筛选(filter)结果集或改变排序方式(sort)。典型场景包括:搜索 typeahead(自动补全):结果列表由用户输入的搜索词动态过滤;切换评论排序模式:对某篇文章的评论,用户可选择不同的排序方式,服务器返回完全不同的评论集合;改变信息流(News Feed)的排序方式。本文要解决的问题正是:当这些“筛选参数”变化时,如何让已渲染的连接数据重新获取(refetch),同时不影响已有的分页(pagination)能力。本文基于仓库中 refetching-connections.md 编写,并结合当前仓库中 Relay 17 版本的源码进行深入验证。一、连接字段如何接收筛选参数在 GraphQL 层面,连接字段可以接收任意参数来筛选或排序结果。例如,下面的 fragment 请求了一个按order_by排序、按search_term过滤的好友列表:fragment UserFragment on User { name friends(order_by: DATE_ADDED, search_term: "Alice", first: 10) { edges { node { name age } } } }其中order_by、search_term是筛选/排序参数,而first是分页参数(表示取前 10 条)。first、after、before、last这类分页参数由 Relay 自动管理,但筛选参数则由业务逻辑控制。在 Relay 中,我们通常不把筛选参数写死成字面量,而是通过 GraphQL 变量(variables)传入,这样运行时才能动态改变它们。使用usePaginationFragment时,fragment 中可以这样写:type Props = { userRef: FriendsListComponent_user$key, }; function FriendsListComponent(props: Props) { const userRef = props.userRef; const {data, ...} = usePaginationFragment( graphql` fragment FriendsListComponent_user on User { name friends( order_by: $orderBy, search_term: $searchTerm, after: $cursor, first: $count, ) @connection(key: "FriendsListComponent_user_friends_connection") { edges { node { name age } } } } `, userRef, ); return (...); }这里有几个关键点:$orderBy、$searchTerm是筛选变量,$cursor、$count是分页变量;@connection(key: "FriendsListComponent_user_friends_connection")指令告诉 Relay 这是一个需要规范化的连接字段,key用于在 Relay store 中唯一标识该连接(详见 transform_connections.rs 中build_connection_metadata等实现);使用usePaginationFragment的前提是 fragment 同时被标注@refetchable(queryName: "..."),编译器会为该 fragment 自动生成一个分页查询(可参考 pagination.md 中的完整写法)。二、分页时筛选参数会被保留usePaginationFragment返回的loadNext(count)用于加载下一页。一个容易忽视但至关重要的行为是:调用loadNext时,会继续使用初始查询时的筛选参数值。type Props = { userRef: FriendsListComponent_user$key, }; function FriendsListComponent(props: Props) { const {data, loadNext} = usePaginationFragment( graphql` fragment FriendsListComponent_user on User { name friends(order_by: $orderBy, search_term: $searchTerm) @connection(key: "FriendsListComponent_user_friends_connection") { edges { node { name age } } } } `, userRef, ); return ( <> <h1>Friends of {data.name}:</h1> <List items={data.friends?.nodes}>{...}</List> {/* Loading the next items will use the original order_by and search_term values used for the initial query */} <Button onClick={() => loadNext(10)}>Load more friends</Button> </> ); }注意:调用loadNext会沿用初始查询时的order_by和search_term值。在分页过程中,这些值不会(也不应该)改变——因为后续页面必须与第一页处于同一筛选条件下,分页才有意义。从源码看,这一行为由 useLoadMoreFunction.js 中的loadMore回调实现:它从 fragment selector 中取出parentVariables和fragmentVariables合并为baseVariables,再调用getPaginationVariables生成分页请求的变量:const parentVariables = fragmentSelector.owner.variables; const fragmentVariables = fragmentSelector.variables; const extraVariables = options?.UNSTABLE_extraVariables; const baseVariables = { ...parentVariables, ...fragmentVariables, }; const paginationVariables = getPaginationVariables( direction, count, cursor, baseVariables, {...extraVariables}, paginationMetadata, );而 getPaginationVariables.js 只会覆盖cursor和count这两个分页变量,其余变量(包括orderBy、searchTerm)原样保留在baseVariables中,从而实现“分页沿用原筛选条件”的效果。同时它会将反向分页的游标/数量变量置为null,确保 forward/backward 两种方向互不干扰:const paginationVariables = { ...baseVariables, ...extraVariables, [forwardMetadata.cursor]: cursor, [forwardMetadata.count]: count, }; if (backwardMetadata && backwardMetadata.cursor) { paginationVariables[backwardMetadata.cursor] = null; } if (backwardMetadata && backwardMetadata.count) { paginationVariables[backwardMetadata.count] = null; }需要临时在分页请求上附加额外变量时,可以使用loadNext(count, {UNSTABLE_extraVariables: {...}}),但注意不能把cursor、count放进extraVariables——源码中对此有显式warning(见 getPaginationVariables.js)。三、用 refetch 以不同的参数重新获取连接如果我们需要用不同的变量重新获取连接——比如用户改变了搜索词或排序方式——就不能再用loadNext,而应使用usePaginationFragment提供的refetch函数。其用法与 Refetching Fragments with Different Data 一节中介绍的useRefetchableFragment的refetch完全一致。3.1 完整示例:搜索词变化时自动重取/** * FriendsListComponent.react.js */ import type {FriendsListComponent_user$key} from 'FriendsListComponent_user.graphql'; const React = require('React'); const {useState, useEffect} = require('React'); const {graphql, usePaginationFragment} = require('react-relay'); type Props = { searchTerm?: string, user: FriendsListComponent_user$key, }; function FriendsListComponent(props: Props) { const searchTerm = props.searchTerm; const {data, loadNext, refetch} = usePaginationFragment( graphql` fragment FriendsListComponent_user on User { name friends( order_by: $orderBy, search_term: $searchTerm, after: $cursor, first: $count, ) @connection(key: "FriendsListComponent_user_friends_connection") { edges { node { name age } } } } `, props.user, ); useEffect(() => { // When the searchTerm provided via props changes, refetch the connection // with the new searchTerm refetch({first: 10, search_term: searchTerm}, {fetchPolicy: 'store-or-network'}); }, [searchTerm]) return ( <> <h1>Friends of {data.name}:</h1> {/* When the button is clicked, refetch the connection but sorted differently */} <Button onClick={() => refetch({first: 10, orderBy: 'DATE_ADDED'}); }> Sort by date added </Button> <List items={data.friends?.nodes}>...</List> <Button onClick={() => loadNext(10)}>Load more friends</Button> </> ); }3.2 提炼关键行为逐条解读上面这个例子:refetch接受新变量集合并重新抓取 fragment。你传入的变量是生成的查询所期望变量的一个子集:生成的查询需要id(当 fragment 类型带有id字段时),以及 fragment 中传递引用的所有其他变量。在本例中,我们必须传first(要抓取的条数),并可传入不同的筛选值如orderBy、searchTerm。注意这里refetch({first: 10, order_by: searchTerm})的写法来自文档示例;实际项目中变量名以你在 fragment 中声明的$orderBy/$searchTerm为准。可能触发 Suspense:refetch会重新渲染组件,如果需要发起并等待网络请求,组件可能进入 suspend 状态(详见 Loading States with Suspense)。因此必须确保组件上层有Suspense边界来展示 fallback。refetch 是“从零开始”:调用refetch在概念上等于从头重新抓取连接,并重置分页状态。例如用不同的search_term抓取后,之前search_term的分页信息不再有意义——我们实际上是在对一个全新列表进行分页。3.3 fetchPolicy 的选择示例中使用了fetchPolicy: 'store-or-network'。Relay 17 中合法的fetchPolicy取值在 RelayRuntimeTypes.js 中定义为:fetchPolicy行为'store-or-network'若 store 中已有满足条件的数据则直接复用(跳过网络请求),否则发起网络请求'store-and-network'先读 store 数据立即渲染,同时发起网络请求,请求完成后用最新数据更新'network-only'始终发起网络请求,不读缓存'store-only'只读 store,绝不发起网络请求若希望避免 Suspense:可先用fetchQuery把数据写入 store,再以fetchPolicy: 'store-only'调用refetch,此时数据已缓存、不会 suspend(完整做法见 refetching-fragments-with-different-data.md 的 "If you need to avoid Suspense" 小节)。refetch还支持UNSTABLE_renderPolicy: 'full' | 'partial'(对应 RelayRuntimeTypes.js 的RenderPolicy)与onComplete回调,类型定义见 useRefetchableFragmentInternal.js。四、源码视角:refetch 与分页是如何协同的usePaginationFragment本质上是useRefetchableFragmentInternal(负责refetch)与useLoadMoreFunction(负责loadNext/loadPrevious)的组合。查看 usePaginationFragment.js 可以看到:通过getPaginationMetadata从 fragment 中解析连接路径与分页元数据;通过useRefetchableFragmentInternal获得refetch能力;分别构造 forward / backward 两个方向的loadNext/loadPrevious;关键点:refetchPagination在真正调用refetch之前,会先disposeFetchNext()和disposeFetchPrevious()——即取消所有在途的分页请求:const refetchPagination = useCallback( (variables: TVariables, options: void | Options) => { disposeFetchNext(); disposeFetchPrevious(); return refetch(variables, {...options, __environment: undefined}); }, [disposeFetchNext, disposeFetchPrevious, refetch], );这从源码层面印证了文档中的结论:refetch 会重置分页状态——旧的分页请求被主动作废,连接从第一条开始重新获取。再看 useRefetchableFragmentInternal.js 中refetch的实现,变量合并顺序为:const refetchVariables: VariablesOf<TQuery> = { ...(parentVariables as $FlowFixMe), ...fragmentVariables, ...providedRefetchVariables, };即:未在refetch调用中提供的变量,会从原 fragment owner 的变量中继承。因此你不需要每次把所有变量都传齐,只需要传“发生变化的”以及必要的first(count)即可。同时,如果生成的查询需要标识符(如id)且未显式传入,Relay 会从 fragment data 中读取并自动填充:if ( identifierInfo != null && !providedRefetchVariables.hasOwnProperty( identifierInfo.identifierQueryVariableName, ) ) { refetchVariables[identifierInfo.identifierQueryVariableName] = identifierValue; }相关运行时期类型与getRefetchMetadata、getPaginationMetadata的返回结构可参考 getPaginationMetadata.js 与relay-runtime的 index.d.ts。@connection指令在编译器侧的处理(连接元数据构建、handle field 生成)位于 transform_connections.rs。五、实践清单与常见陷阱综合文档与源码,给出可复制的实践要点:fragment 需要@refetchable:usePaginationFragment要求 fragment 可 refetch(作用于Viewer、Query、实现Node的类型或@fetchable类型),并配合@connection(key: "...")标注连接字段。筛选变量通过 GraphQL 变量传入:不要把筛选条件写死成字面量,否则无法在运行时改变它们。分页沿用原筛选条件:loadNext/loadPrevious会复用初始查询的筛选变量,不要在分页时试图改变筛选条件。改变筛选条件用refetch:传入新变量(必要时含first),Relay 会自动继承未变化的变量、填充id,并取消在途分页请求、重置分页游标。务必有Suspense边界:refetch可能触发 suspend,组件上层需要Suspense包裹;想避免 Suspense 时使用fetchQuery+fetchPolicy: 'store-only'的组合。变量命名一致性:GraphQL 变量名($orderBy/$searchTerm/$cursor/$count)必须在 fragment 中声明并在查询根(@refetchable生成查询)中可用;生成的查询会要求id(若类型有id字段)以及 fragment 中传递引用的所有其他变量。延伸阅读分页入门与loadNext/hasNext用法:pagination.md连接(Connection)概念与游标分页:connections.md通用 fragment 重取(useRefetchableFragment):refetching-fragments-with-different-data.md高级分页(自定义 append/prepend 行为):advanced-pagination.md运行时实现:usePaginationFragment.js、useRefetchableFragmentInternal.js、getPaginationVariables.js赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址:https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐OpenHermes-2.5-neural-chat-7b-v3-1-7B深度解析:Mistral架构融合模型如何革新AI对话体验OpenHermes 2.5 neural chat 7b v3 1 7B深度解析:Mistral架构融合模型如何革新AI对话体验 想要体验 Mistral架构前端开发工具Relay 连接(Connection)重取指南:使用与变更筛选条件(usePaginationFragment 的 refetch 实战解析)Relay 连接(Connection)重取指南:使用与变更筛选条件(usePaginationFragment 的 refetch 实战解析) 在 Relay前端开发工具Relay 连接数据重新获取(Refetching Connections):使用 usePaginationFragment 实现过滤与排序变量切换Relay 连接数据重新获取(Refetching Connections):使用 usePaginationFragment 实现过滤与排序变量切换 导读 在前端开发工具
网站建设 2026/9/23 4:44:48 研发增长:从各环节改善,提高产品市场竞争力 研发增长:从各环节改善,提高产品市场竞争力——专知智库研发增长系列引言:研发增长的最终检验标准,是产品竞争力研发增长,不是账面上的数字游戏。它的最终检验标准只有一个:产品在市场上,能不能… 李华
网站建设 2026/9/23 4:43:46 排气系统声学设计实战:从噪声原理到消声结构与NVH调校 一台车在你心里的“性格”,一半是动力给的,另一半其实是声音给的。排气声浪厚不厚、有没有破音、急加速时是沉闷有力还是干瘪嘶吼,这些都不是玄学,而是排气系统声学设计的结果。我做了几十个项目的排气噪声优化,从乘用… 李华
网站建设 2026/9/23 4:42:38 基于ANSYS的混凝土细观三相模型建模:多面体骨料密堆积与ITZ界面过渡区实现 做混凝土数值模拟的朋友应该都有同感:宏观模型算起来快,但很多宏观现象解释不了。裂缝从哪来、怎么穿过骨料还是沿界面走、砂浆和骨料的协同变形怎么分配——这些问题都得往细观尺度去看。我一直在做这方面的研究,最近完成了一个含界面过渡区… 李华
网站建设 2026/9/23 4:36:03 Java递归中Scanner资源管理的最佳实践 1. 问题背景与核心痛点在Java开发中,递归算法与Scanner资源管理的结合使用一直是个容易被忽视的细节问题。很多开发者都遇到过这样的场景:在递归方法中读取用户输入时,程序运行后出现java.util.NoSuchElementException异常,或者发… 李华
网站建设 2026/9/23 4:35:40 提示词做减法:GPT-6与Skills分工的实战指南 最近OpenAI官方关于GPT-6与Skills方向放出的指导,核心观点就一句话:提示词该做减法了。这对过去两年习惯了“长提示词等于高质量”的人来说,几乎是方向性急转弯。我在GPT-6上做了几轮实测,又把自己手上十几个项目的提示词逐条拆开… 李华
网站建设 2026/9/23 4:33:08 DGL 实现 CARE-GNN:面向伪装欺诈检测器的抗伪装图神经网络实战指南 人工智能机器学习深度学习图计算 【免费下载链接】dgl Python package built to ease deep learning on graph, on top of existing DL frameworks. 项目地址: https://gitcode.com/gh_mirrors/dg/dgl 点击查看 免费下载 本文基于 DGL 官方仓库中的 caregnn 示例&a… 李华