【问题标题】:Updating react state with GraphQL queries [duplicate]使用 GraphQL 查询更新反应状态 [重复]
【发布时间】: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 我更新了我的答案(现在我可以访问桌面了)——它可能会有所帮助。

标签: reactjs graphql react-hooks apollo react-apollo


【解决方案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 中的 useQueryuseLazyQuery 示例,您会注意到它们通常只是在 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 请随意在此处粘贴您的组件代码,以确保发生了什么。
    猜你喜欢
    • 1970-01-01
    • 2020-08-31
    • 2021-11-14
    • 2017-12-02
    • 1970-01-01
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    相关资源
    最近更新 更多