如何在组件外调用 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 用于嵌套反应组件的突变?

如何在功能性 React JS 组件中获取数据?

如何在 Redux Reducer 中使用 GraphQL Mutations

谈谈 GraphQL 的历史组件和生态系统

如何在 React 中使用 Redux Store 和 GraphQl 进行查询