我正在使用react-apollo
查询graphQL服务器,并能够成功地将数据与客户端混合。由于将有不止一个地方,因此我将查询数据,我试图创建一个容器(重构)以封装useQuery
钩子,以便可以在一个地方使用。
第一次尝试(按预期方式工作)
const HomeContainer = () => {
const { data, error, loading } = useQuery(GET_DATA_QUERY, {
variables: DATA_VARIABLES
});
const [transformedData, setTransformedData] = useState();
useEffect(() => {
if(!!data) {
const transformedData = someTransformationFunc(data);
setTransformedData(...{transformedData});
}
}, [data]);
if (loading) {
return <div>Loading data ...</div>;
}
if (error) {
return <p>Error loading data</p>;
}
if (!data) {
return <p>Not found</p>;
}
return <Home transformedData={transformedData} />;
};
我想将查询的不同阶段周围的仪式封装到一个新的容器(加载,错误状态)中,以便减少代码重复。
重构时的第一刺
query
,variables
和callback
中传递。这有责任根据查询的状态(加载,错误或没有数据返回时)返回不同的节点。const HomeContainer = () => {
const {data, error, loading} = useQuery(GET_DATA_QUERY, {
variables: DATA_VARIABLES
});
const [transformedData, setTransformedData] = useState();
const callback = (data) => {
const transformedData = someTransformationFunc(data);
setTransformedData(...{
transformedData
});
};
return (
<QueryContainer
query={GET_DATA_QUERY}
variables={DATA_VARIABLES}
callback ={callback}
>
<Home transformedData={transformedData} />
</QueryContainer>
)
};
const QueryContainer = ({callback, query, variables, children }) => {
const {data, error, loading } = useQuery(query, {
variables: variables
});
// Once data is updated invoke the callback
// The transformation of the raw data is handled by the child
useEffect(() => {
if (!!data) {
callback(data);
}
}, [data]);
if (loading) {
return <div > Loading data... < /div>;
}
if (error) {
return <p > Error loading data < /p>;
}
if (!data) {
return <p > Not found < /p>;
}
return children;
};
QueryContainer
正在使用useEffect
,并在callback
返回时调用data
。我觉得这有点混乱,并且无法实现封装在父级中并使用callback
来交谈和更新子级的目的。
第三次尝试(将孩子用作函数)
摆脱了回调并将数据作为第一个参数传递给children
函数。
const HomeContainer = () => {
return (
<QueryContainer
query={GET_DATA_QUERY}
variables={DATA_VARIABLES}
>
{(data) => {
const transformedData = someTransformationFunc(data);
return <Home transformedData={transformedData} />;
}}
</QueryContainer>
)
};
const QueryContainer = ({ query, variables, children }) => {
const { data, error, loading } = useQuery(query, {
variables: variables
});
if (loading) {
return <div>Loading data ...</div>;
}
if (error) {
return <p>Error loading data</p>;
}
if (!data) {
return <p>Not found</p>;
}
return children(data);
};
我希望它能正常工作,因为当data
更新时,新渲染将以data
作为参数将子级作为函数调用。但是当我导航到该路线时,我会看到黑屏(没有错误,并且可以看到正确的数据记录到控制台中)如果再次单击该链接,则可以看到已提交给DOM的组件。
不太确定这里发生了什么,想知道是否有人可以对这里发生的事情有所了解。
为什么不使用React Context?我会说这将更容易,更有效。
只需创建一个新文件Context.js
或任何其他文件,您就可以在这里查询您的数据并根据需要传递它。这将是您的容器:
// Context.js
import React, { createContext } from "react"
import { useQuery } from "@apollo/react-hooks"
import gql from "graphql-tag"
export const QueryContext = createContext()
const QueryContextProvider = ({ children }) => {
const { loading, error, data } = useQuery(gql`
{
...
}
`)
// Passing the data to the Context provider.
// You can pass any state needed.
return (
<QueryContext.Provider
value={{
loading,
error,
data
}}
>
{children}
</QueryContext.Provider>
)
}
export default QueryContextProvider
您现在需要使用此上下文包装您的应用程序:
// App.js
import QueryContextProvider from "./Context"
const client = new ApolloClient({
uri: "your end point"
})
const App = () => {
return (
<ApolloProvider client={client}>
<QueryContextProvider>
// Your components ...
<SomeComponent />
</QueryContextProvider>
</ApolloProvider>
)
}
export default App
现在您可以通过整个应用程序获取数据。像这样消耗它:
// SomeComponent.js
import React, { useContext } from "react"
import { QueryContext } from "../Context"
const SomeComponent = () => {
const { loading, error, data } = useContext(QueryContext)
if (loading) return <p>Loading...</p>
if (error) return <p>Error :(</p>
return data.map(({ args.. }) => (
<div>
...
</div>
))
}