使用 GraphQL 提取的 useEffect 无限循环

useEffect infinite loop with GraphQL fetch

我正在编写一个简单的反应挂钩,它从 GraphQL 端点获取一些数据,将结果存储在状态中,并在获取数据时 returns [fetched-data, true][null, false]没有数据,因此您可以在 const [data, loaded] = useGraphql({ query, variables }); return loaded ? data : 'loading'.

等组件中使用它

我遇到的问题是,如果我声明参数对象的可选变量 属性,挂钩会导致无限的 useEffect 循环。如果我省略变量参数,一切正常。

export default function useGraphql({ query, variables, token }: QueryArgs) {
    const [data, setData] = useState(null)

    const fetchData = useCallback(async () => {
        const init: RequestInit = {
            method: "POST",
            headers: {
                [token && 'Authorization']: token && `bearer ${token}`,
                "Content-Type": "application/json",
                "Accept": "application/json"
            },
            body: JSON.stringify({
                query, variables
            })
        }

        try {
            const response: Response = await fetch('/graphql', init)

            const parsed = await response.json()

            setData(parsed.data)
        } catch (error) {
            console.log(error)

            setData(null)
        }
    }, [query, variables, token]) // if I remove variables from the array here, this doesn't cause a loop anymore

    useEffect(() => {
        let mounted = true

        if (mounted) {
            fetchData()
        }

        return () => {
            mounted = false

            setData(null)
        }
    }, [fetchData])

    if (data) {
        for (const prop in data) {
            return [data[prop], true]

            break;
        }
    }

    return [null, false]
} 

当然我在组件中使用这样的钩子:

const [data, loaded] = useGraphql({ 
        query: `query ($email: String!) {
            singleUser(email: $email) {
                username
                email
            }
        }`,
        variables: {
            email: "user1@project" // if I put variables like this, the loop occurs. If I hardcode the variables in the query, there is no loop.
        }
    })

    return <span> {loaded ? data.email : 'loading'} </span>

我真的不明白为什么放置变量会导致无限重新渲染。 variables 参数如何改变 fetchData 函数?

我现在想到了以下解决方案:

我没有传递纯变量对象,而是使用以下代码检查它是否已更改,return 结果作为字符串。

function updateObj(obj: object): string {
    let object: string = JSON.stringify(obj)

    if (object !== JSON.stringify(obj)) {
        object = JSON.stringify(obj)
    }

    return object
}

所以在我的 useGraphql 钩子中,我使用它:

const updatedVariables = updateObj(variables) 

它正在做它的工作。我尝试 return 一个对象而不是一个字符串化的对象,但是解析结果(JSON.parse())仍然导致循环