如何在组件外调用 GraphQL
Posted
技术标签:
【中文标题】如何在组件外调用 GraphQL【英文标题】:How to call GraphQL outside a component 【发布时间】:2019-10-13 21:47:23 【问题描述】:我使用 Query 组件制作了一堆调用 GraphQL 的 React 组件,一切正常。
在一个组件中,我需要从数据库中获取一些初始数据,但没有任何可视化表示。
我尝试使用查询组件,但它似乎只在渲染周期触发。我曾尝试将其打包成一个函数,并在需要数据的组件中调用该函数。但是代码/查询没有执行,因为没有要显示的组件。
如何在没有组件的情况下从数据库中获取这些数据?
我找不到任何有关如何解决此问题的文档。但我不能 唯一这样做的人。
ApolloConsumer 或 ApolloProvider 是我的问题的答案吗?
我正在处理会议和会议。一个会议持续了几天,每天都有许多会议。
我想要实现的是每天呈现一个包含 X 个标签的页面。每个选项卡代表一天,并显示当天的会话数。
我的会话页面:
import React from 'react';
import FullWidthTabs from '../components/Sessions';
import SessionTab from '../components/SessionTab';
import BwAppBar2 from '../components/BwAppBar2';
import ConferenceDays from '../components/ConferenceDays';
class SessionsPage extends React.Component
static async getInitialProps()
console.log("GetInitProps SessionsPage");
render()
let a = ConferenceDays();
return (
<div>
<BwAppBar2 />
a
<FullWidthTabs days=['2018-06-11', '2018-06-12', '2018-06-13'] day1= < SessionTab conferenceId = "57" day = '2018-06-11' / >
day2= < SessionTab conferenceId = "57" day = '2018-06-12' / > day3= < SessionTab conferenceId = "57" day = '2018-06-13' / > >
</FullWidthTabs>
</div>
);
export default (SessionsPage);
这里的日期已经硬编码在页面中,只是为了测试。
但是为了知道会议跨越多少天,我必须找到会议并确定开始和结束日期并生成其间的所有日期:
import React, Component from 'react'
import graphql from 'react-apollo'
import Query from 'react-apollo'
import gql from 'graphql-tag'
import Link from '@material-ui/core/Link';
import useQuery from "react-apollo-hooks";
import conferencesQuery from '../queries/conferences'
import Table, Head, Cell from './Table'
import ConferenceCard from './ConferenceCard';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import moment from 'moment';
const CONFERENCE_QUERY = gql`
query conference($conferenceId : ID!)
conference(id: $conferenceId)
title
start_date
end_date
`
let index = 0;
let loopDate = 0;
let dates = [];
let conferenceId = 57;
const ConferenceDays = () => (
<Query query=CONFERENCE_QUERY variables=conferenceId>
( loading, error, data ) =>
if (loading)
return <div>Fetching</div>
if (error)
return <div>Error</div>
const startDate = moment(data.conference.start_date, 'x');
const endDate = moment(data.conference.end_date, 'x');
for (loopDate = parseInt(data.conference.start_date);
loopDate < parseInt(data.conference.end_date);
loopDate += 86400000)
let aDate = moment(loopDate, 'x');
dates.push(aDate.format('YYYY-MM-DD').toString());
console.log(dates);
return(dates);
</Query>);
export default ConferenceDays
但是这种方法不正确吗?
在层次结构中提升 ConferenceDates 组件是否更正确?
金
【问题讨论】:
【参考方案1】:您可以将 ApolloClient
的创建分离到一个单独的文件中,并使用 init 函数在 React 组件之外访问客户端。
import React from 'react';
import
ApolloClient,
HttpLink,
InMemoryCache,
from "@apollo/client";
let apolloClient;
const httpLink = new HttpLink(
uri: "http://localhost:4000/graphql",
credentials: "same-origin",
);
function createApolloClient()
return new ApolloClient(
link: httpLink,
cache: new InMemoryCache(),
);
export function initializeApollo()
const _apolloClient = apolloClient ?? createApolloClient();
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
export function useApollo()
const store = useMemo(() => initializeApollo(initialState), [initialState]);
return store;
然后你会像这样使用这个外部组件:
const client = initializeApollo()
const res = await client.query(
query: MY_QUERY,
variables: ,
)
我自己没有尝试过,但我认为这是您对如何进行此操作以及如何访问 ApolloClient
的一个想法。
【讨论】:
【参考方案2】:如果您使用的是函数式组件,则可以在函数中使用 useApolloClient 钩子,就好像它不是钩子一样。
import useApolloClient, gql from "@apollo/client";
MY_QUERY = gql'
query OUR_QUERY
books
edges
node
id
title
author
'
const myFunctionalComponent = () => // outside function component
const client = useApolloClient();
const aNormalFunction = () => // please note that this is not a component
client.query(
query: MY_QUERY,
fetchPolicy: "cache-first" // select appropriate fetchPolicy
).then((data) =>
console.log(data) //do whatever you like with the data
).catch((err) =>
console.log(err)
)
;
// just call it as a function whenever you want
aNormalFunction()
// you can even call it conditionally which is not possible with useQuery hook
if (true)
aNormalFunction()
return (
<p>Hello Hook!</>
);
;
export default myFunctionalComponent;
【讨论】:
这看起来很有希望,但我在 Visual Studio 代码中收到此警告:React Hook "useApolloClient" is called in function "getPostTagId" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. eslint(react-hooks/rules-of-hooks)
对不起瑞恩,我的错误。我展示的示例没有显示上下文。实际上,“aNormalFunction”应该留在函数组件中。只有这样,才能声明 useApolloClient 钩子。我会更新代码。
如果 aNormalFunction 保留在函数组件中,那么您仍在使用钩子。那么这可能如何工作呢?
我在这里想说的是,我现在可以随时调用查询,而加载和错误处理在函数内部进行管理。 useQuery 和 useLazyQuery 与组件的生命周期相关联,因此您需要在组件的生命周期内处理“加载”和“错误”状态。换句话说,我提到的方法是“异步”到组件的生命周期事件。【参考方案3】:
ApolloClient
有一个mutate
方法。您只需导入您的 Apollo 客户端实例并调用 apolloClient.mutate
。
【讨论】:
【参考方案4】:在 React 组件中,您可以使用 useLazyQuery
from Apollo Client,它只会在您调用它时触发。然后将它传递给你的函数并在那里调用它。
【讨论】:
【参考方案5】:对于使用urql
作为 GraphQL 客户端的人-
const graphqlClient = createClient(
url: '',
fetchOptions: () =>
return
headers:
;
);
const fetchCountries = () => (dispatch: Dispatch) =>
graphqlClient
.query(countriesQuery, getCountriesVariable())
.toPromise()
.then(result =>
dispatch(
type: UPDATE_COUNTRIES,
payload: result.data.countries
);
if (result.error?.message)
// https://formidable.com/open-source/urql/docs/basics/errors/
logErrorMessage(result.error?.message, 'AppActions.fetchCountries');
)
.catch(error =>
logError(error, 'AppActions.fetchCountries');
);
;
文档:https://formidable.com/open-source/urql/docs/api/core/#clientquery
【讨论】:
以上是关于如何在组件外调用 GraphQL的主要内容,如果未能解决你的问题,请参考以下文章
如何测试graphql apollo组件?如何在 compose() 中获得完整的覆盖?
Apollo/graphQL:如何将乐观 UI 用于嵌套反应组件的突变?