九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
?如何使用 GraphQL 構建 TypeScript React 應用

GraphQL 和 TypeScript 的使用率都在爆炸式增長,并且當兩者與 React 結合應用時,它們在一起可以創(chuàng)造理想的開發(fā)體驗。本文將引導你使用公共的 SpaceX GraphQL API,使用 React 和 Apollo 構建一個客戶端應用程序,展示有關火箭發(fā)射的信息。

GraphQL 和 TypeScript 的使用率都在爆炸式增長,并且當兩者與 React 結合應用時,它們在一起可以創(chuàng)造理想的開發(fā)體驗。

GraphQL 改變了我們對 API 的思考方式;利用 GrahpQL 直觀的鍵 / 值對匹配,客戶端可以精確請求所需的數據來顯示在網頁或移動應用屏幕上。TypeScript 則向變量添加了靜態(tài)類型來擴展 JavaScript,從而減少了錯誤并提高了可讀性。

本文將引導你使用公共的 SpaceX GraphQL API,使用 React 和 Apollo 構建一個客戶端應用程序,展示有關火箭發(fā)射的信息。我們將自動為查詢生成 TypeScript 類型,并使用 React Hooks 執(zhí)行這些查詢。

假定你對 React、GraphQL 和 TypeScript 有所了解,我們將重點介紹如何將它們集成在一起以構建一個正常運作的應用程序。

如果你在哪里卡住了,可以參考源代碼 [1] 或查看應用的演示 [2]。

為什么選擇 GraphQL+TypeScript?

GraphQL API 需要被強類型化,并且從單個端點提供數據??蛻舳嗽诖硕它c上調用一個 GET 請求,就可以接收一個后端的完全自注釋的表示,以及所有可用數據和相應的類型。

我們可以使用 GraphQL Code Generator[3] 在 Web 應用目錄中掃描查詢文件,并將它們與 GraphQL API 提供的信息匹配,從而為所有請求數據創(chuàng)建 TypeScript 類型。使用 GraphQL,我們可以免費自動輸入 React 組件的 props。這樣可以減少錯誤,并加快產品迭代速度。

開始工作
我們將使用帶有 TypeScript 設置的 create-react-app 來引導我們的應用程序。執(zhí)行以下命令來初始化你的應用:
npx create-react-app graphql-typescript-react --typescript
// NOTE - you will need Node v8.10.0+ and NPM v5.2+

使用 --typescript 標志,CRA 將生成你的文件以及.ts 和.tsx,并將創(chuàng)建一個 tsconfig.json 文件。

導航到應用目錄:
cd graphql-typescript-react

現在我們可以安裝其他依賴項。我們的應用將使用 Apollo 來執(zhí)行 GraphQL API 請求。Apollo 所需的庫是 apollo-boost、react-apollo、react-apollo-hooks、graphql-tag 和 graphql。

apollo-boost 包含查詢 API 和在內存中本地緩存數據所需的工具;react-apollo 為 React 提供綁定;react-apollo-hooks 將 Apollo 查詢包裝在一個 React Hook 中;graphql-tag 用于構建我們的查詢文檔;graphql 是一個對等依賴項,提供了 GraphQL 實現的詳細信息。
yarn add apollo-boost react-apollo react-apollo-hooks graphql-tag graphql
graphql-code-generator 用于自動執(zhí)行我們的 TypeScript 工作流程。我們將安裝 codegen CLI 以生成所需的配置和插件。
yarn add -D @graphql-codegen/cli
執(zhí)行以下命令來設置代碼生成配置:
$(npm bin)/graphql-codegen init

這將啟動 CLI 向導。請執(zhí)行以下步驟:

  1. 使用 React 構建應用程序。
  2. Schema 位于 https://spacexdata.herokuapp.com/graphql。
  3. 將你的操作和分片(fragments)位置設置為./src/components/**/*.{ts,tsx}這樣它將在我們所有的 TypeScript 文件中搜索查詢聲明。
  4. 使用默認插件“TypeScript”“TypeScript Operations”“TypeScript React Apollo”。
  5. 使用目標 src/Generated/graphql.tsx(react-apollo 插件需要.tsx)。
  6. 不要生成內省文件。
  7. 使用默認的 codegen.yml 文件。
  8. 運行腳本是 codegen。

現在,在 CLI 中運行 yarn 命令,安裝 CLI 工具添加到 package.json 中的插件。

我們還將對 codegen.yml 文件進行一次更新,這樣它還將添加 withHooks: true 配置選項來生成類型化的 React Hook 查詢。你的配置文件應如下所示:
overwrite: true
schema: 'https://spacexdata.herokuapp.com/graphql'
documents: './src/components/**/*.ts'
generates:
  src/generated/graphql.tsx:
    plugins:
      - 'typescript'
      - 'typescript-operations'
      - 'typescript-react-apollo'
    config:
      withHooks: true
編寫 GraphQL 查詢并生成類型

GraphQL 的一大好處是它使用了聲明性數據獲取。我們能夠編寫出一些與使用它們的組件并存的查詢,并且 UI 能夠準確地請求它需要渲染的內容。

使用 REST API 時,我們需要查找處于(或不處于)最新狀態(tài)的文檔。如果 REST 出現任何問題,我們需要針對 API 和 console.log 結果發(fā)起請求以調試數據。

GraphQL 允許你在 UI 中訪問 URL,查看完全定義的 schema 并針對它執(zhí)行請求,從而解決了這個問題。請查看要使用的數據 [4]。

盡管我們有大量的 SpaceX 數據可供使用,但我們僅顯示有關火箭發(fā)射的信息。我們將有兩個主要組件:

  1. 一個 launches 列表,用戶可以單擊列表以了解有關發(fā)射的更多信息。
  2. 單次 launch 的詳細資料。

對于第一個組件,我們將查詢 launches 鍵,并請求 flight_number、mission_name 和 launch_year。我們將這些數據顯示在一個列表中,當用戶單擊其中一個項目時,我們將根據 launch 鍵查詢關于這次火箭發(fā)射的更大數據集。下面我們在 GraphQL 游樂場中測試我們的第一個查詢。

要編寫查詢時,我們首先創(chuàng)建一個 src/components 文件夾,然后創(chuàng)建一個 src/components/LaunchList 文件夾。在此文件夾中,創(chuàng)建 index.tsx、LaunchList.tsx、query.ts 和 styles.css 文件。在 query.ts 文件中,我們可以從游樂場傳輸查詢并將其放在一個 gql 字符串中。
import gql from 'graphql-tag';
export const QUERY_LAUNCH_LIST = gql`
  query LaunchList {
    launches {
      flight_number
      mission_name
      launch_year
    }
  }
`;

我們的其他查詢將基于 flight_number,獲得有關單次發(fā)射的更詳細數據。由于這將通過用戶交互動態(tài)生成,因此我們將需要使用 GraphQL 變量。我們還可以在游樂場上用變量測試查詢。

在查詢名稱旁邊指定變量,前面帶上 $ 及其類型。然后你就可以在 body 內使用變量了。針對查詢,我們通過傳遞 $id 變量(其類型為 String!)來設置火箭發(fā)射的 ID。

我們將 id 作為一個變量傳遞,該變量對應于 LaunchList 查詢中的 flight_number。LaunchProfile 查詢還將包含嵌套的對象 / 類型,在這里我們可以在方括號內指定鍵來獲取值。

例如,發(fā)射信息包含了一個 rocket 定義(LaunchRocket 類型),我們將要求它提供 rocket_name 和 rocket_type。要了解更多可用于 LaunchRocket 的字段信息,你可以使用側邊的 schema 導航器來了解可用數據。

現在將這個查詢轉移到我們的應用程序中。使用 index.tsx、LaunchProfile.tsx、query.ts 和 styles.css 文件創(chuàng)建 src/components/LaunchProfile 文件夾。在 query.ts 文件中,我們從游樂場粘貼查詢。
import gql from 'graphql-tag';
export const QUERY_LAUNCH_PROFILE = gql`
  query LaunchProfile($id: String!) {
    launch(id: $id) {
      flight_number
      mission_name
      launch_year
      launch_success
      details
      launch_site {
        site_name
      }
      rocket {
        rocket_name
        rocket_type
      }
      links {
        flickr_images
      }
    }
  }
`;
現在我們已經定義了查詢,你終于可以生成 TypeScript 接口和類型化的 Hooks。在你的終端中執(zhí)行:
yarn codegen

在 src/generation/graphql.ts 內部,你將找到定義應用程序所需的所有類型,以及用于獲取 GraphQL 端點以檢索該數據的對應查詢。

這個文件通常會很大,但是充滿了有價值的信息。我建議花些時間瀏覽一下,并了解我們的 codegen 完全基于 GraphQL schema 所創(chuàng)建的所有類型。

比如說檢查 type Launch,它是 GraphQL 的 Launch 對象的 TypeScript 表示形式,我們會在游樂場上與之交互。還可以滾動到文件的底部,查看專門為我們將要執(zhí)行的查詢生成的代碼——它已創(chuàng)建了組件、HOC、類型化的 props/ 查詢和類型化的 hooks。

初始化 Apollo 客戶端

在 src/index.tsx 中,我們需要初始化 Apollo 客戶端,并使用 ApolloProvider 組件將我們的 client 添加到 React 的上下文中。我們還需要 ApolloProviderHooks 組件以在 hooks 中啟用上下文。

我們初始化一個 new ApolloClient 并為其提供 GraphQL API 的 URI,然后將<App />組件包裝在上下文提供程序中。你的索引文件應如下所示:

import React from 'react';
import ReactDOM from 'react-dom';
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from 'react-apollo';
import { ApolloProvider as ApolloHooksProvider } from 'react-apollo-hooks';
import './index.css';
import App from './App';
const client = new ApolloClient({
  uri: 'https://spacexdata.herokuapp.com/graphql',
});
ReactDOM.render(
  <ApolloProvider client={client}>
    <ApolloHooksProvider client={client}>
      <App />
    </ApolloHooksProvider>
  </ApolloProvider>,
  document.getElementById('root'),
);
構建我們的組件

現在我們已經準備好了通過 Apollo 執(zhí)行 GraphQL 查詢所需的一切內容。

在 src/components/LaunchList/index.tsx 內,我們將創(chuàng)建一個函數組件,其使用生成的 useLaunchListQuery hook。查詢 hooks 返回 data、loading 和 error 值。我們將檢查容器組件中的 loading 和 error,并將 data 傳遞給我們的演示組件。

我們將此組件用作一個容器 / 智能組件,從而保持關注點的分離;我們還將數據傳遞給表示 / 啞組件,該組件僅顯示給出的內容。我們還將在等待數據時顯示基本的加載和錯誤狀態(tài)。

你的容器組件應如下所示:
import * as React from 'react';
import { useLaunchListQuery } from '../../generated/graphql';
import LaunchList from './LaunchList';
const LaunchListContainer = () => {
  const { data, error, loading } = useLaunchListQuery();
  if (loading) {
    return <div>Loading...</div>;
  }
  if (error || !data) {
    return <div>ERROR</div>;
  }
  return <LaunchList data={data} />;
};
export default LaunchListContainer;

我們的演示組件將使用我們的類型化 data 對象來構建 UI。我們使用<ol>創(chuàng)建一個有序列表,然后映射到發(fā)射信息中,以顯示 mission_name 和 launch_year。

我們的 src/components/LaunchList/LaunchList.tsx 將如下所示:
import * as React from 'react';
import { LaunchListQuery } from '../../generated/graphql';
import './styles.css';
interface Props {
  data: LaunchListQuery;
}
const className = 'LaunchList';
const LaunchList: React.FC<Props> = ({ data }) => (
  <div className={className}>
    <h3>Launches</h3>
    <ol className={`${className}__list`}>
      {!!data.launches &&
        data.launches.map(
          (launch, i) =>
            !!launch && (
              <li key={i} className={`${className}__item`}>
                {launch.mission_name} ({launch.launch_year})
              </li>
            ),
        )}
    </ol>
  </div>
);
export default LaunchList;

如果你使用的是 VS Code,由于我們正在使用 TypeScript,因此 IntelliSense 會準確顯示可用的值并提供自動完成列表。它還會警告我們正在使用的數據可以為 null 還是 undefined。

這么神奇?編輯器會自動幫我們編程。另外,如果需要定義類型或函數,可以按 Cmd + t,鼠標指針懸停其上,它將為你提供所有詳細信息。

我們還將添加一些 CSS 樣式,這些樣式將顯示我們的項目并允許它們在列表溢出時滾動。在 src/components/LaunchList/styles.css 中添加以下代碼:
.LaunchList {
  height: 100vh;
  overflow: hidden auto;
  background-color: #ececec;
  width: 300px;
  padding-left: 20px;
  padding-right: 20px;
}
.LaunchList__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.LaunchList__item {
  padding-top: 20px;
  padding-bottom: 20px;
  border-top: 1px solid #919191;
  cursor: pointer;
}

現在我們將構建配置組件,以顯示有關火箭發(fā)射的更多詳細信息。該組件的 index.tsx 文件基本是一樣的,只是我們使用的是 Profile 查詢和組件。我們還將一個變量傳遞給我們的 React hook 以獲取發(fā)射 ID。目前我們將其硬編碼為'42',然后在布局好應用后添加動態(tài)功能。

在 src/components/LaunchProfile/index.tsx 內添加以下代碼:
import * as React from 'react';
import { useLaunchProfileQuery } from '../../generated/graphql';
import LaunchProfile from './LaunchProfile';
const LaunchProfileContainer = () => {
  const { data, error, loading } = useLaunchProfileQuery(
    { variables: { id: '42' } }
  );
  if (loading) {
    return <div>Loading...</div>;
  }
  if (error) {
    return <div>ERROR</div>;
  }
  if (!data) {
    return <div>Select a flight from the panel</div>;
  }
  return <LaunchProfile data={data} />;
};
export default LaunchProfileContainer;

現在我們需要創(chuàng)建演示組件。它將在用戶界面頂部顯示火箭發(fā)射的名稱和詳細信息,然后在說明下方顯示一個發(fā)射圖像網格。

src/components/LaunchProfile/LaunchProfile.tsx 組件如下所示:
import * as React from 'react';
import { LaunchProfileQuery } from '../../generated/graphql';
import './styles.css';
interface Props {
  data: LaunchProfileQuery;
}
const className = 'LaunchProfile';
const LaunchProfile: React.FC<Props> = ({ data }) => {
  if (!data.launch) {
    return <div>No launch available</div>;
  }
  return (
    <div className={className}>
      <div className={`${className}__status`}>
        <span>Flight {data.launch.flight_number}: </span>
        {data.launch.launch_success ? (
          <span className={`${className}__success`}>Success</span>
        ) : (
          <span className={`${className}__failed`}>Failed</span>
        )}
      </div>
      <h1 className={`${className}__title`}>
        {data.launch.mission_name}
        {data.launch.rocket &&
          ` (${data.launch.rocket.rocket_name} | ${data.launch.rocket.rocket_type})`}
      </h1>
      <p className={`${className}__description`}>{data.launch.details}</p>
      {!!data.launch.links && !!data.launch.links.flickr_images && (
        <div className={`${className}__image-list`}>
          {data.launch.links.flickr_images.map(image =>
            image ? <img src={image} className={`${className}__image`} key={image} /> : null,
          )}
        </div>
      )}
    </div>
  );
};
export default LaunchProfile;
最后一步是使用 CSS 設置此組件的樣式。將以下內容添加到你的 src/components/LaunchProfile/styles.css 文件中:
.LaunchProfile {
  height: 100vh;
  max-height: 100%;
  width: calc(100vw - 300px);
  overflow: hidden auto;
  padding-left: 20px;
  padding-right: 20px;
}
.LaunchProfile__status {
  margin-top: 40px;
}
.LaunchProfile__title {
  margin-top: 0;
  margin-bottom: 4px;
}
.LaunchProfile__success {
  color: #2cb84b;
}
.LaunchProfile__failed {
  color: #ff695e;
}
.LaunchProfile__image-list {
  display: grid;
  grid-gap: 20px;
  grid-template-columns: repeat(2, 1fr);
  margin-top: 40px;
  padding-bottom: 100px;
}
.LaunchProfile__image {
  width: 100%;
}
現在我們已經完成了組件的靜態(tài)版本,我們可以在 UI 中查看它們。我們會將組件包含在 src/App.tsx 文件中,還會將轉換為一個函數組件。我們使用函數組件來簡化代碼,并在添加單擊功能時允許使用 hooks。
import React from 'react';
import LaunchList from './components/LaunchList';
import LaunchProfile from './components/LaunchProfile';
import './App.css';
const App = () => {
  return (
    <div className='App'>
      <LaunchList />
      <LaunchProfile />
    </div>
  );
};
export default App;
為了獲得想要的樣式,我們將 src/App.css 更改為以下內容:
.App {
  display: flex;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}

在終端中執(zhí)行 yarn start,在瀏覽器中轉至 http://localhost:3000,你就應該能看到應用的基本版本了!

添加用戶交互

現在我們需要添加一項功能,以在用戶單擊面板中的項目時獲取完整的火箭發(fā)射相關數據。我們將在 App 組件中創(chuàng)建一個 hook 來跟蹤火箭 ID,并將其傳遞給 LaunchProfile 組件以重新獲取發(fā)射相關數據。

我們在 src/App.tsx 中添加 useState 來維護和更新 ID 的狀態(tài)。當用戶從列表中選擇一個 ID 時,我們還將使用名為 handleIdChange 的 useCallback 作為單擊處理程序來更新 ID。我們將這個 id 傳遞給 LaunchProfile,然后將 handleIdChange 傳遞給<LaunchList />。

更新后的<App />組件現在應如下所示:
const App = () => {
  const [id, setId] = React.useState(42);
  const handleIdChange = React.useCallback(newId => {
    setId(newId);
  }, []);
  return (
    <div className='App'>
      <LaunchList handleIdChange={handleIdChange} />
      <LaunchProfile id={id} />
    </div>
  );
};
在 LaunchList.tsx 組件內部,我們需要為 handleIdChange 創(chuàng)建一個類型并將其添加到 props 解構中。然后在<li>火箭項目上,我們將在 onClick 回調中執(zhí)行該函數。
export interface OwnProps {
  handleIdChange: (newId: number) => void;
}
interface Props extends OwnProps {
  data: LaunchListQuery;
}
// ...
const LaunchList: React.FC<Props> = ({ data, handleIdChange }) => (

// ...
<li
  key={i}
  className={`${className}__item`}
  onClick={() => handleIdChange(launch.flight_number!)}
>

在 LaunchList/index.tsx 內部,請確保導入 OwnProps 聲明以類型化要傳遞到容器組件的 props,然后將這些 props 散布到<LaunchList data = {data} {... props} />中。

最后一步是在 id 更改時 refetch 數據。在 LaunchProfile/index.tsx 文件中,我們將使用 useEffect 來管理 React 的生命周期,并在 id 更改時觸發(fā)一個 fetch。以下是實現 fetch 所需的唯一更改:
interface OwnProps {
  id: number;
}
const LaunchProfileContainer = ({ id }: OwnProps) => {
  const { data, error, loading, refetch } = useLaunchProfileQuery({
    variables: { id: String(id) },
  });
  React.useEffect(() => {
    refetch();
  }, [id]);
由于我們已將演示與數據分離,因此無需對<LaunchProfile />組件進行任何更新;我們只需要更新 index.tsx 文件,以便所選的 flight_number 在更改時重新獲取完整的火箭發(fā)射相關數據。
現在你已經完成了它!如果按照這些步驟操作,應該能做出來一個功能齊全的 GraphQL 應用。如果你迷路了,可以在源代碼中找到可行的解決方案。
   小結   

配置好應用后,我們可以看到開發(fā)速度是非??斓摹N覀兛梢暂p松構建數據驅動的 UI。GraphQL 允許我們定義組件中所需的數據,并且可以將其無縫用作組件中的 props。生成的 TypeScript 定義為我們編寫的代碼提供了極高的信心水平。

如果你希望深入研究該項目,那么下一步將是使用 API中的額外字段來添加分頁和更多的數據連接。要對火箭發(fā)射列表進行分頁,你需要獲得當前列表的長度,并將 offset 變量傳遞給 LaunchList 查詢。

我鼓勵你更深入地研究它并編寫自己的查詢,以鞏固本文提出的概念。

本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
2019年前端的3個趨勢
搭建Typescript+React項目模板(3)
Vite React 組件開發(fā)實踐
Typescript結合React實踐
在Vue項目中使用Typescript
前端框架及組件庫選型分析
更多類似文章 >>
生活服務
熱點新聞
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服