Consumindo API Graphics EM反応.アポロクライアント


エッセは、セグンダe lima parte da sのreのcomo montarのmaのaplicaを得ます.Aqui VAOS Construir馬Aplicaは、oを正面から見ます.
パラSeguir com ESSEのチュートリアルでは、エヌレスリオリオのnoは、デデGraphql、アルのMデ反応.jsPIA SE Maritharizar COM Graphel , Veja O seguinte artigo :
📝 GraphQL: O que é e como usar
SE Quiser ver o o primeiroチュートリアルデcomo montar uma api graphql , veja o seguinte artigo :
📝 Montando API GraphQL em Node.js com Apollo e MongoDB
Voc - pode acompanhar oチュートリアルpasso - passo - ou - clonar oリポジトリ
EUのDISIONIBIZIZI UMA VERのオンライン(SEMの突然変異をパラシュートで降下してください.
projeto関連のリンク集

  • ディゴーのgithubgithub.com/emerson-pereira/frutas

  • オンラインのDA APIgraphql-frutas.herokuapp.com

  • Verのオンラインアプリケーションの反応:codesandbox.io/s/graphql-frutas-4isf8
  • プロポラ


    Opera Ses - es es crudとしてのpropostaウェブサイトsobre frutas onde podemos gerenciar os dados fazendo.oサイトser feti‐em反応.Seridor EMノード.jsNESSEチュートリアルdesenvolveremos oフロントエンドEMは反応します.js

    スタック


    フロントエンド、teremos :
  • フレームワークReact.js アパルカの法則

  • Apollo Client for React.js パラの消費者dados da api graphql emは反応します.js
  • iniciandoアプリ反応。js


    アクディDevemos連続体fruits デondeは、エーモスのチュートリアルの前に来ます.Dentro delaは、o seguinte comandoパラを実行しますiniciar um projeto react :
    npx create-react-app frontend
    
    Quando terminado o processo、umaパスタfrontend Torは、sido criada comをaplicaを与えます.js
    📦fruits
    ┣ 📂backend
    ┣ 📂frontend
    ┃ ┣ …
    
    アブラアムターミナルデコメディドエNavegueパラパスタfruits/frontend . <資料>
    npm start
    
    Deverは、Abirをテレフォニージェラダcomがつくって、アプリNAポルタ3000に反応させます
    http://localhost:3000

    Aplica哀愁o iniciada!
    アンテデ来られたArmos、FerramentaがつくっているアプリケーションCria Alalgs Arquivos queÑo ser ser o o necess - e rios aqui、como ArquivosデTeste e Configuraは、サービス労働者である.アペクス・トゥイスス・アーキヴィス『セギンテ・エストラチュラ』において
    📂frontend
     ┣ 📂public
     ┃ ┣ 📜favicon.ico
     ┃ ┣ 📜index.html
     ┣ 📂src
     ┃ ┣ 📜App.css
     ┃ ┣ 📜App.js
     ┃ ┣ 📜index.css
     ┃ ┣ 📜index.js
     ┣ 📜.gitignore
     ┣ 📜package.json
     ┗ 📜README.md
    
    アゴラVamos「Limpar」Alguns ArquivosリモコンAlgumas chamadas eデミアCoisas desnecess und rias.
    安藤アンナパスタpublic , アブラindex.html デシネメネラ
    カミナーfrontend/public/index.html
    <!DOCTYPE html>
    <html lang="pt-BR">
      <head>
        <meta charset="utf-8" />
        <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1"
        />
        <meta
          name="description"
          content="Um app sobre informações nutricionais de frutas."
        />
        <title>Frutas</title>
      </head>
      <body>
        <noscript>
          You need to enable JavaScript to run this app.
        </noscript>
        <div id="root"></div>
      </body>
    </html>
    
    アゴラ、vamos ados onos onesonque que ser o o usado nesta aplicasrc , 代用OSのコンテindex.cssApp.css COM os seguintes conte api ddos :
    カミナーfrontend/src/index.css
    body {
      margin: 0;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
        "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
        "Droid Sans", "Helvetica Neue", sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }
    
    input,
    button {
      padding: 10px;
      font-size: calc(10px + 1vmin);
    }
    
    button:hover {
      cursor: pointer;
    }
    
    ul {
      list-style: none;
      margin: 20px 0;
      padding: 0;
    }
    
    li {
      display: flex;
      justify-content: space-between;
      align-items: baseline;
      padding: 10px;
      margin: 10px;
    }
    
    カミナーfrontend/src/App.css
    .App {
      text-align: center;
    }
    
    .App-header {
      background-color: #282c34;
      color: white;
      position: absolute;
      top: 10%;
      right: 0;
      width: 100vw;
    }
    .App-header h1 {
      margin: 0;
      padding: 20px;
    }
    
    .App-body {
      background-color: #282c34;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      font-size: calc(10px + 2vmin);
      color: white;
    }
    
    .App-viewbox {
      position: relative;
    }
    
    .App-close-btn {
      position: absolute;
      top: -100px;
      right: -100px;
    }
    
    .App-close-btn button {
      background: none;
      border: 0;
      color: white;
      font-size: calc(10px + 2vmin);
    }
    
    .App-btn {
      max-width: 120px;
      width: 100%;
    }
    
    .App-btn.secondary {
      background: transparent;
      border: 2px solid white;
      color: white;
    }
    
    .App-item-actions {
      margin-left: 40px;
    }
    
    .App-item-actions a {
      margin: 0 10px;
      background: none;
      text-decoration: none;
    }
    
    .App-item-actions a:hover {
      cursor: pointer;
    }
    
    見積もりadicionados.アゴラバモスパスタindex.js デロンデsrc E証明書que o Arquivo Eest
    カミナーfrontend/src/index.js
    import React from "react"
    import ReactDOM from "react-dom"
    import "./index.css"
    import App from "./App"
    
    ReactDOM.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>,
      document.getElementById("root")
    )
    
    Eアゴラ、oは、lmsを得ます.src/App.js ダ・セギンテ
    カミナーfrontend/src/App.js
    import React from "react"
    import "./App.css"
    
    function App() {
      return (
        <div className="App">
          <div className="App-header">
            <h1>Frutas</h1>
          </div>
          <div className="App-body"></div>
        </div>
      )
    }
    
    export default App
    
    アゴラSalve tudo e abraなしnavagador、証明されたque n n o o h h errosなしコンソール.アヴァレア・デカ・フォルマ

    アッシム、結論は、MOSのコンフィギュラの詩は、oイントロDo projeto、vamosアゴラao prは、Ximo passo.

    コンフィギュラント


    パラファイサナバガ・パークス・アンド・セントル・ローラス、ヴァモス・ソウール著React router . COM O Comando :
    npm i react-router-dom
    
    デストロイパスタsrc チャリ・アルArquivoroutes.js Roasデッサ・マネラとしてのE inicie :
    カミナーfrontend/src/routes.js
    import React from "react"
    import {
      BrowserRouter as Router,
      Switch,
      Route,
    } from "react-router-dom"
    
    import Fruits from "./components/Fruits"
    
    const Routes = () => (
      <Router>
        <Switch>
          <Route exact path="/">
            <Fruits />
          </Route>
        </Switch>
      </Router>
    )
    
    export default Routes
    
    所有者path インディアナEm qual caminho da aplicaは、o aquale構成要素serFruits , エステSerは、exibido naホームにdaを与えます.
    アゴラ、バモスクリアーOFruits.js セイントチャンドノArquivoデrotasのque est est.Esse Componentte Mostrarは、Ma Listaデfrutasアッシムcomoとして、形容詞esエデExcbir、Editar e excluirデCada Frutaです.
    デロンデsrc , クリエ麻パスタcomponents . Dentro Desta , Crie o Componentte de Frutas :
    カミナーfrontend/src/components/Fruits.js
    import React from "react"
    import { Link } from "react-router-dom"
    
    const FruitsList = () => {
      return (
        <>
          <ul>
            <li>
              <span>Banana</span>
              <div className="App-item-actions">
                <Link>
                  <span role="img" aria-label="visualizar">
                    👀
                  </span>
                </Link>
                <Link>
                  <span role="img" aria-label="editar">
                    ✏️
                  </span>
                </Link>
                <Link>
                  <span role="img" aria-label="excluir"></span>
                </Link>
              </div>
            </li>
          </ul>
    
          <p>
            <Link>
              <button>Nova Fruta</button>
            </Link>
          </p>
        </>
      )
    }
    
    export default FruitsList
    
    Por enQuanto adicionamosは、UMA Lista com apenas馬Frutaをします.
    タムム・クラーモスLink AO Redor Dosボットは、es、Ma nを得ますo apontamos para Nemhumaロタ、ネッセMomento.FAREMS ISSO MAISはfrenteです.
    アゴラ, v .App.js ロサ・クリダ
    カミナーfrontend/src/App.js
    import React from "react"
    import "./App.css"
    import Routes from "./routes"
    function App() {
      return (
        <div className="App">
          <div className="App-header">
            <h1>Frutas</h1>
          </div>
          <div className="App-body">
            <Routes /> </div>
        </div>
      )
    }
    
    export default App
    
    フランチャス・クリタダ・アパレ・ナ・テル・インフォーマッド・ダ・アプシカについて
    アゴラ、O PRは、残念なことです.

    API API API


    vamosは、DepEdのNCIASパラUSARアポロクライアントとしてAR Ar Instalando来る.
    NOTA:Aqui Estamos usandoアポロクライアントNA Verは、3です.
    npm i @apollo/client graphql
    

  • @アポロ/クライアント:Pacoteアポロcom o

  • Graphics Optical Do Graphics COM A l . GICAパラParsearクエリ
  • アゴラ、Efetuamosは、コネElement o o usando A URL DA APIバックエンドです.como estamos desenvolvendo tudo localmente , vamos fornecer urlローカルDOバックエンドqueはna porta 4000になります.
    カミナーfrontend/src/App.js
    import React from "react"
    import { ApolloProvider, ApolloClient, InMemoryCache,} from "@apollo/client"import "./App.css"
    import Routes from "./routes"
    
    const client = new ApolloClient({ uri: "http://localhost:4000", cache: new InMemoryCache(),})
    function App() {
      return (
        <ApolloProvider client={client}> <div className="App">
            <div className="App-header">
              <h1>Frutas</h1>
            </div>
            <div className="App-body">
              <Routes />
            </div>
          </div>
        </ApolloProvider> )
    }
    
    export default App
    
    アゴラ・バモスFruits.js E人気のO Componentte COM dados vindos da api usando oアポロクライアント.
    カミナーfrontend/src/components/Fruits.js
    import React from "react"
    import { gql, useQuery } from "@apollo/client"import { Link } from "react-router-dom"
    
    export const GET_FRUITS = gql` { fruits { id name } }`
    const FruitsList = () => {
      const { loading, error, data } = useQuery(GET_FRUITS) if (loading) return <p>Loading...</p> if (error) return <p>Error :(</p>
      return (
        <>
          <ul>
            {data.fruits && data.fruits.map(({ name, id }) => ( <li key={id}> <span>{name}</span> <div className="App-item-actions"> <Link to={`/fruit/${id}`}> <span role="img" aria-label="visualizar"> 👀 </span> </Link> <Link to={`/editFruit/${id}`}> <span role="img" aria-label="editar"> ✏️ </span> </Link> <Link to={`/deleteFruit/${id}`}> <span role="img" aria-label="excluir"></span> </Link> </div> </li> ))} </ul>
    
          <p>
            <Link to="/createFruit"> <button>Nova Fruta</button>
            </Link>
          </p>
        </>
      )
    }
    
    export default FruitsList
    
    Eは、アッシム、fizemosクエリe populamos o構成要素com dados da APIを簡素化します.アイダfizemos um retorno単純なao ao suは、eデErro、カソOcorra藻類を積んでいるリオcomフィードバックに乗ります.
    アルM M disso、デAntemは、o、apontamos rotasCRUD レフレシダダ.vamos,agora,criar osのコンポーネントをcada a poo o o o para depois conectar caada rota se seu sontivo componentについて述べた.

    ファサード


    パラセグアA OrdemはACRをしますNimo、Vamosは、ar Ar com o ComponentteデCria

    クリエイト


    カミナーfrontend/src/components/CreateFruit.js
    import React from "react"
    import { gql, useMutation } from "@apollo/client"
    import { Link, useHistory } from "react-router-dom"
    import { GET_FRUITS } from "./Fruits"
    
    const CREATE_FRUIT = gql`
      mutation UpdateFruit(
        $name: String!
        $sugar: String!
        $calories: String!
      ) {
        createFruit(
          fruit: {
            name: $name
            nutritions: { sugar: $sugar, calories: $calories }
          }
        ) {
          id
          name
          nutritions {
            calories
            sugar
          }
        }
      }
    `
    
    const CreateFruit = () => {
      const history = useHistory()
    
      const [createFruit, { loading, error }] = useMutation(
        CREATE_FRUIT,
        {
          update(cache, { data: { createFruit } }) {
            const { fruits } = cache.readQuery({ query: GET_FRUITS })
            cache.writeQuery({
              query: GET_FRUITS,
              data: { fruits: fruits.concat([createFruit]) },
            })
          },
          onCompleted() {
            history.push(`/`)
          },
        }
      )
    
      if (loading) return <p>Loading...</p>
      if (error) return <p>Error :(</p>
    
      let nameInput
      let sugarInput
      let caloriesInput
    
      return (
        <div>
          <form
            className="App-viewbox"
            onSubmit={e => {
              e.preventDefault()
    
              createFruit({
                variables: {
                  name: nameInput.value,
                  sugar: sugarInput.value,
                  calories: caloriesInput.value,
                },
              })
    
              nameInput.value = ""
              sugarInput.value = ""
              caloriesInput.value = ""
            }}
          >
            <p>
              <label>
                Fruta
                <br />
                <input
                  type="text"
                  name="name"
                  ref={node => {
                    nameInput = node
                  }}
                />
              </label>
            </p>
            <p>
              <label>
                Açucar (g)
                <br />
                <input
                  type="text"
                  name="sugar"
                  ref={node => {
                    sugarInput = node
                  }}
                />
              </label>
            </p>
            <p>
              <label>
                Calorias
                <br />
                <input
                  type="text"
                  name="calories"
                  ref={node => {
                    caloriesInput = node
                  }}
                />
              </label>
            </p>
            <p className="App-close-btn">
              <Link to="/">
                <button></button>
              </Link>
            </p>
            <p>
              <button className="App-btn" type="submit">
                Salvar
              </button>
            </p>
          </form>
        </div>
      )
    }
    
    export default CreateFruit
    
    Neste Componentte Criamos UMA Fustususando突然変異、E atualizamos Oキャッシュは、アポロReusezando A質問をしますGET_FRUITS エクストラFruits.js . パラエンフェンダーマリスsobre esse assuntoコンサルタントdocumentação do Apollo client sobre mutations .
    アルム・M・ディソ、タム・プ・ム・トマス・ヴァンテゲムonCompleted パラRedirecionarは、pのp . ginaパラシュートで降下します.

    読める


    アゴラ,批評家の構成要素
    カミナーfrontend/src/components/Fruit.js
    import React from "react"
    import { gql, useQuery } from "@apollo/client"
    import { useParams, Link } from "react-router-dom"
    
    export const GET_FRUIT_BY_ID = gql`
      query GetFruit($id: ID!) {
        fruit(id: $id) {
          id
          name
          nutritions {
            sugar
            calories
          }
        }
      }
    `
    
    const Fruit = () => {
      const { id } = useParams()
      const { loading, error, data } = useQuery(GET_FRUIT_BY_ID, {
        variables: { id },
      })
    
      if (loading) return <p>Loading...</p>
      if (error) return <p>Error :(</p>
    
      return (
        <div className="App-viewbox">
          <p>
            <strong>Fruta: </strong>
            {data.fruit.name}
          </p>
          <p>
            <strong>Açucar: </strong>
            {data.fruit.nutritions.sugar}g
          </p>
          <p>
            <strong>Calorias: </strong>
            {data.fruit.nutritions.calories}kcal
          </p>
          <p className="App-close-btn">
            <Link to="/">
              <button></button>
            </Link>
          </p>
          <p>
            <Link to={`/editFruit/${id}`}>
              <button>Editar</button>
            </Link>
          </p>
        </div>
      )
    }
    
    export default Fruit
    
    オペラ座劇としてのアクィナスid da FrutaペラURL da rota usandouseParams ドゥー反応ルータ.

    更新


    E ,パラO成分
    カミナーfrontend/src/components/EditFruit.js
    import React from "react"
    import { gql, useQuery, useMutation } from "@apollo/client"
    import { useParams, Link, useHistory } from "react-router-dom"
    import { GET_FRUIT_BY_ID } from "./Fruit"
    
    const UPDATE_FRUIT = gql`
      mutation UpdateFruit(
        $id: String!
        $name: String
        $sugar: String
        $calories: String
      ) {
        updateFruit(
          id: $id
          fruit: {
            name: $name
            nutritions: { sugar: $sugar, calories: $calories }
          }
        ) {
          id
          name
          nutritions {
            calories
            sugar
          }
        }
      }
    `
    
    const EditFruit = () => {
      const { id } = useParams()
      const history = useHistory()
    
      const { loading, error, data } = useQuery(GET_FRUIT_BY_ID, {
        variables: { id },
      })
      const [updateFruit, { error: mutationError }] = useMutation(
        UPDATE_FRUIT,
        {
          onCompleted() {
            history.push(`/`)
          },
        }
      )
    
      if (loading) return <p>Loading...</p>
      if (error || mutationError) return <p>Error :(</p>
    
      let nameInput
      let sugarInput
      let caloriesInput
    
      return (
        <div>
          <form
            className="App-viewbox"
            onSubmit={e => {
              e.preventDefault()
    
              updateFruit({
                variables: {
                  id: data.fruit.id,
                  name: nameInput.value,
                  sugar: sugarInput.value,
                  calories: caloriesInput.value,
                },
              })
            }}
          >
            <p>
              <label>
                Fruta
                <br />
                <input
                  type="text"
                  name="name"
                  defaultValue={data.fruit.name}
                  ref={node => {
                    nameInput = node
                  }}
                />
              </label>
            </p>
            <p>
              <label>
                Açucar (g)
                <br />
                <input
                  type="text"
                  name="sugar"
                  defaultValue={data.fruit.nutritions.sugar}
                  ref={node => {
                    sugarInput = node
                  }}
                />
              </label>
            </p>
            <p>
              <label>
                Calorias
                <br />
                <input
                  type="text"
                  name="calories"
                  defaultValue={data.fruit.nutritions.calories}
                  ref={node => {
                    caloriesInput = node
                  }}
                />
              </label>
            </p>
            <p className="App-close-btn">
              <Link to="/">
                <button type="button"></button>
              </Link>
            </p>
            <p>
              <button className="App-btn" type="submit">
                Salvar
              </button>
            </p>
          </form>
        </div>
      )
    }
    
    export default EditFruit
    
    Aqui Tambは、m usamosを運びますid da Fruta e Redirecionamosは、ホームDepoisデFinalizadoをパラパラします.アッシムcomo usamos質問GET_FRUIT_BY_ID <資料>可視化の構成要素

    削除


    E , Pra Finalizar , Criaremos O Componentite de Dele Personiés O de Fruta
    カミナーfrontend/src/components/DeleteFruit.js
    import React from "react"
    import { gql, useQuery, useMutation } from "@apollo/client"
    import { useParams, Link, useHistory } from "react-router-dom"
    import { GET_FRUITS } from "./Fruits"
    import { GET_FRUIT_BY_ID } from "./Fruit"
    
    const DELETE_FRUIT = gql`
      mutation DeleteFruit($id: String) {
        deleteFruit(id: $id) {
          id
          name
          nutritions {
            calories
            sugar
          }
        }
      }
    `
    
    const DeleteFruit = () => {
      const history = useHistory()
      const { id } = useParams()
    
      const { loading, error, data } = useQuery(GET_FRUIT_BY_ID, {
        variables: { id },
      })
    
      const [deleteFruit, { error: mutationError }] = useMutation(
        DELETE_FRUIT,
        {
          update(cache) {
            const { fruits } = cache.readQuery({ query: GET_FRUITS })
    
            const deletedIndex = fruits.findIndex(
              fruit => fruit.id === id
            )
            const updatedCache = [
              ...fruits.slice(0, deletedIndex),
              ...fruits.slice(deletedIndex + 1, fruits.length),
            ]
            cache.writeQuery({
              query: GET_FRUITS,
              data: {
                fruits: updatedCache,
              },
            })
          },
          onCompleted() {
            history.push(`/`)
          },
        }
      )
    
      if (loading) return <p>Loading...</p>
      if (error || mutationError) return <p>Error :(</p>
    
      return (
        <div>
          <form
            className="App-viewbox"
            onSubmit={e => {
              e.preventDefault()
    
              deleteFruit({
                variables: { id },
              })
            }}
          >
            <p>
              Excluir <strong>{data.fruit.name}</strong>?
            </p>
            <p className="App-close-btn">
              <Link to="/">
                <button></button>
              </Link>
            </p>
            <p>
              <button className="App-btn" type="submit">
                Excluir
              </button>
            </p>
          </form>
        </div>
      )
    }
    
    export default DeleteFruit
    
    Aqui Tamb Em m mは、アポロクライアントです.Depois de Removido o item , removemos o mesmo item do cache e relacionamosクエリGET_FRUITS のCOM dados atualizados.
    CRUD feito com sucesso!
    <研究ノート>アポロ・クリエンテ『メイレス・デタール』の文献目録
    🔗 www.apollographql.com/docs/react

    リガートロッツ


    アゴラパラFinalizar、Ligamos Cada rotaは、seu component enteです.
    カミナーfrontend/src/routes.js
    import React from "react"
    import {
      BrowserRouter as Router,
      Switch,
      Route,
    } from "react-router-dom"
    
    import Fruits from "./components/Fruits"
    import Fruit from "./components/Fruit"import CreateFruit from "./components/CreateFruit"import EditFruit from "./components/EditFruit"import DeleteFruit from "./components/DeleteFruit"
    const Routes = () => (
      <Router>
        <Switch>
          <Route exact path="/">
            <Fruits />
          </Route>
          <Route path="/fruit/:id"> <Fruit /> </Route> <Route path="/createFruit"> <CreateFruit /> </Route> <Route path="/editFruit/:id"> <EditFruit /> </Route> <Route path="/deleteFruit/:id"> <DeleteFruit /> </Route> </Switch>
      </Router>
    )
    
    export default Routes
    

    結論


    E ESSE foo oチュートリアル、Nesta Jornada Vocは、aprendeuを得ます:
  • Graphel E comoの利用
  • COMO Montar Tam API EM Graphical UsandoノードアポロサーバE
  • コモモンタルマタアップラカは、oフロントフロントパラ消費者API graphql com反応します.アポロクライアント.
  • エスパーターTeアジャドド!
    projeto関連のリンク集

  • ディゴーのgithubgithub.com/emerson-pereira/frutas

  • オンラインのDA APIgraphql-frutas.herokuapp.com

  • Verのオンラインアプリケーションの反応:codesandbox.io/s/graphql-frutas-4isf8
  • オリジナルマンemersonpereira.me