使用 GraphQL 查询更新反应状态 [重复]
Posted
技术标签:
【中文标题】使用 GraphQL 查询更新反应状态 [重复]【英文标题】:Updating react state with GraphQL queries [duplicate] 【发布时间】:2020-04-23 14:33:52 【问题描述】:我正在尝试使用从 GraphQL 查询中收集的数据更新状态。 我尝试过类似于https://www.apollographql.com/docs/react/data/queries/ 标题为“手动执行查询”下的官方教程。
export default function SearchField()
const [
searchInput,
setSearchInput
] = useState('');
const [
drinks,
setDrinks
] = useState(null);
const handleSubmit = (e) =>
e.preventDefault();
getDrinks( variables: cocktailName: searchInput );
;
const [
getDrinks,
loading, data
] = useLazyQuery(MULTIPLE_COCKTAILS_BY_NAME_QUERY);
if (loading)
return (
<div className="justify-content-center">
<p>Loading ...</p>
</div>
);
if (data && data.multipleCocktailsByName)
setDrinks(data.multipleCocktailsByName);
return (...)
return 之前的最后一部分使组件崩溃,并显示“错误:重新渲染过多。React 限制了渲染次数以防止无限循环。”如果我尝试像这样更新 handleSubmit 中的状态:
const handleSubmit = (e) =>
e.preventDefault();
getDrinks( variables: cocktailName: searchInput );
setDrinks(data.multipleCocktailsByName);
;
然后我收到错误“无法读取未定义的属性 'multipleCocktailsByName'”。我仍然设法有条件地从查询中呈现数据,但处理状态很麻烦。我非常感谢您能帮我解决这个问题。
【问题讨论】:
useLazyQuery
不应使用,除非您需要执行查询以响应用户操作(如点击)。不要通过自己管理状态来重新发明***——使用 useQuery
代替已经将数据公开为状态,除了加载和错误状态。
感谢您的意见,丹尼尔!我省略了函数的返回部分以使问题更具可读性。但是,在这种情况下,我想通过按一下按钮来使用查询,所以lazyQuery 似乎是正确的选择。
你应该使用钩子提供的data
。您可以按照其中一个答案的建议使用useEffect
将其填充到组件状态,但这会不必要地使您的代码复杂化。
@ZeGeR 我更新了我的答案(现在我可以访问桌面了)——它可能会有所帮助。
【参考方案1】:
您当前组件的问题是if
语句(就在return
之前)将满足标准并在每次渲染时更新状态(一旦提交表单并执行useQuery
- 如data
现在有值了)。
由于无论如何都是懒加载数据,所以只需要在提交表单时更新组件的状态即可。将 if
语句移动到 handleSubmit
函数应该可以解决您的问题,并且不需要任何副作用 (useEffect
)。
我在handleSubmit
函数上使用了async/await
,以确保在更新状态之前查询已经完成。
export default function SearchField()
const [
searchInput,
setSearchInput
] = useState('');
const [
drinks,
setDrinks
] = useState(null);
const [
getDrinks,
loading, data
] = useLazyQuery(MULTIPLE_COCKTAILS_BY_NAME_QUERY);
const handleSubmit = async (e) =>
e.preventDefault();
await getDrinks( variables: cocktailName: searchInput );
if (data && data.multipleCocktailsByName)
setDrinks(data.multipleCocktailsByName);
;
if (loading)
return (
<div className="justify-content-center">
<p>Loading ...</p>
</div>
);
return (...)
接下来要考虑的一点是,您是否真的需要drinks
状态变量?如果您查看 Apollo 中的 useQuery
和 useLazyQuery
示例,您会注意到它们通常只是在 return 语句的 JSX 中直接使用 data
变量。
这通常涉及一些条件渲染,尽管这在 React 组件中很常见。如果您在 return 语句以外的任何地方都没有使用 drinks
,请考虑重构以取出 useState
调用并为自己节省一两次重新渲染。
【讨论】:
【参考方案2】:在:
if (data && data.multipleCocktailsByName)
setDrinks(data.multipleCocktailsByName);
成功运行查询并填充数据后,您将陷入无限循环,将饮料设置为该值。
您可以只使用数据作为组件状态(忘记饮料),然后:
if (loading) ... // render loading UI
if (error) ... // I suggest adding this so you can better debug.
if (data) ... // do what you intended to do with drinks but calling data.multipleCocktailsByName.
或者,您可以在“数据”上使用 useEffect 挂钩来设置饮料状态,但这似乎没有必要。
我应该补充一点,您的第二个示例失败了,因为调用是异步的,因此数据不会是您在调用后立即得到的数据。这就是为什么,如果你想要一个饮料状态,你会使用一个使用效果钩子,比如:
useEffect(() =>
if (data && data.multipleCocktailsByName)
setDrinks(data.multipleCocktailsByName
,[data, setDrinks])
只有当数据更改并包含值时才会设置饮料。
【讨论】:
感谢您的帮助!我尝试了您建议的 useEffect 解决方案,但不幸的是它导致了另一个错误。 “React Hook “useEffect” 是有条件地调用的。React Hooks 必须在每个组件渲染中以完全相同的顺序调用 react-hooks/rules-of-hooks”。我不确定为什么会收到此错误。 将你的 useEffect 钩子移动到组件的顶部,你可能在你的 useEffect 之前的某个条件内有一个 return 语句,这意味着它只有在你之前的 if 语句不正确时才会有条件地调用。一般来说,在运行任何逻辑之前,请始终将钩子放在组件的开头。您可以在此处阅读有关规则的更多信息:reactjs.org/docs/hooks-rules.html#explanation 请随意在此处粘贴您的组件代码,以确保发生了什么。以上是关于使用 GraphQL 查询更新反应状态 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
React Apollo:从组件状态动态更新 GraphQL 查询
使用 graphql 在 react-native 中重新获取查询