Day 24


複数のクエリー方法


Query機能はクエリだけでなく、さまざまな方法で使用されます.UseQueryは、ページが開いたときにロードするだけでなく、ボタンを押した後や特定の場所で値をロードすることもできます.
例として、UserQuery、UserLazyQuery、およびUserApolloClientを使用します.
ログインの例
電子メール、パスワードを送信してログインを要求し、タグをグローバル状態に保存します.
fetchUserLoggedinによるログイン情報の取得(query)
しかし、ログイン情報の取得を要求しなければ?
const result = fetchUserLoggedIn()
setUserInfo(result.data?.fetchUserLoggedIn)
ユーザー登録情報をresultに入れて持ってきてもらえますか?
->useQueryを直接使用することはできませんが、そうしなければなりません.
const result = await axios.get(「koreanjson.com/post/1」)このように必要な場所でuseQueryを必要とする
このような用途に使用できるのはUserApolloClientで、私は必要な場所でAPIを要求することができます.
useApolloClientの適用を試みる
機能内
const client = useApolloClient()
まだあります.
import {useApolloClient } from "@apollo/client";
これはaxiosのように使用できます.
const resultUserInfo = await client.query({
        query: FETCH_USER_LOGGED_IN
      })
      // 물론 FETCH_USER_LOGGED_IN의 gql은 가져와야한다.
      setUserInfo(resultUserInfo.data.fetchUserLoggedIn)
      import { GlobalContext, setUserInfo} from "../_app";
setuUserInfoというグローバルコンテキストに送信します(インポートも必要です).
また、ログイン成功画面では、グローバルコンテキストのみがインポートされます.
import { useContext } from "react";
import { withAuth } from "../../src/commons/hocs/withAuth";
import { GlobalContext } from "../_app";




function LoginSuccessPage() {
  const { setAccessToken, setUserInfo } = useContext(GlobalContext);

  return (
    <>
      <div>로그인에 성공하였습니다!!!!!</div>
      <div>{userInfo?.name}님 환영합니다!!!</div>
    </>
  );
}
export default withAuth(LoginSuccessPage);
でもまだだめどうして.
ログインするとタグが受信され、タグはグローバルコンテキストとして使用されます.
その後FETCH USER LOGGED INが作動するが、結果は得られない
これまでHeaders:{authorization:Bearer ${myAcessToken}}
だから今は強制的に入れることができます.
import { ChangeEvent, useContext, useState } from "react";
import { gql, useApolloClient, useMutation } from "@apollo/client";
import {
  IMutation,
  IMutationLoginUserArgs,
} from "../../src/commons/types/generated/types";
import { GlobalContext } from "../_app";
import { useRouter } from "next/router";

const LOGIN_USER = gql`
  mutation loginUser($email: String!, $password: String!) {
    loginUser(email: $email, password: $password) {
      accessToken
    }
  }
`;

const FETCH_USER_LOGGED_IN = gql`
  query fetchUserLoggedIn {
    fetchUserLoggedIn {
      email
      name
      picture
    }
  }
`;

export default function LoginPage() {
  const router = useRouter();
  const { setAccessToken, setUserInfo } = useContext(GlobalContext);

  const [myEmail, setMyEmail] = useState("");
  const [myPassword, setMyPassword] = useState("");
  const [loginUser] = useMutation<
    Pick<IMutation, "loginUser">,
    IMutationLoginUserArgs
  >(LOGIN_USER);

  const client = useApolloClient();

  function onChangeMyEmail(event: ChangeEvent<HTMLElement>) {
    setMyEmail(event.target.value);
  }

  function onChangeMyPassword(event: ChangeEvent<HTMLElement>) {
    setMyPassword(event.target.value);
  }

  async function onClickLogin() {
    const result = await loginUser({
      variables: {
        email: myEmail,
        password: myPassword,
      },
    });
    const accessToken = result.data?.loginUser.accessToken || "";
    localStorage.setItem("accessToken", accessToken);
    setAccessToken?.(accessToken); // 여기서 setAccesToken 필요! (글로벌 스테이트에...)

    const resultUserInfo = await client.query({
      query: FETCH_USER_LOGGED_IN,
      context: {
        headers: {
          authorization: `Bearer ${accessToken}`,
        },
      },
    });

    setUserInfo(resultUserInfo.data.fetchUserLoggedIn);
    // 로그인 버튼을 클릭했을 때
    // 이메일과 비밀번호 요청,
    // 그리고 setAccessToken을 글로벌스테이트에 저장
    // fetchUserLoggedin으로 사용자 정보를 받아옴
    // const result = fetchUserLoggedIn()
    // setUserInfo(result.data?.fetchUserLoggedIn)
    // const result = await axios.get("koreanjson.com/post/1")이런 방식으로 원하는 곳에서 useQuery필요
    // 그리고 유저인포도 따로 저장함 <- 이게 안되면 유저 정보가 필요한 곳에 fetch요청을해야함(로그인성공페이지)
    // 그런데 한군데서만 필요한 것이 아님

    // 로그인 성공된 페이지로 이동시키기!!
    router.push("/24-02-login-success");
  }

  return (
    <>
      이메일:
      <input type="text" onChange={onChangeMyEmail} />
      비밀번호:
      <input type="password" onChange={onChangeMyPassword} />
      <button onClick={onClickLogin}>로그인하기!!</button>
    </>
  );
}
では、ログイン完了ページに行きましょう.
context:{
        headers:{
          authorization: `Bearer ${result.data?.loginUser.accessToken}`
        }
      }
複雑に見えるのでアクセスポイントを宣言して入れます.
    const accessToken = result.data?.loginUser.accessToken || "";
完了済みバージョン
import { useContext } from "react";
import { withAuth } from "../../src/commons/hocs/withAuth";
import { GlobalContext } from "../_app";

function LoginSuccessPage() {
  const { userInfo } = useContext(GlobalContext);

  return (
    <>
      <div>로그인에 성공하였습니다!!!!!</div>
      <div>{userInfo?.name}님 환영합니다!!!</div>
    </>
  );
}
export default withAuth(LoginSuccessPage);
useLazyQuery
userQueryは、ページの最初に自動的に要求され、撮影されます.
LazyQueryは、ユーザーが突然変異したように、特定のボタンを押したときに実行します.
その後、fetchUserにアクセスしてデータにアクセスするように要求します.
必要に応じて変数に含めることはできません.
さっきapolloclientは必要な変数に含まれ、必要な場所で実行されました.
Lazyは必要な場所だけ運行しています

3行の概要
useQuery:すべて自動
useLazyQuery:必要な場所で実行
useApolloClient:必要な場所で実行し、必要な変数に含める

フォームライブラリ


React-form
Redux-form
Formik
React-hook-form
色々なライブラリがありましたが、React-hook-formの出現に伴い、徐々に埋もれていきました.
react-hook-formは非制御素子であり、
他の構成部品と比較して、駆動構成部品です.
入力時、制御素子は状態に保存されます
非制御構成部品は、ボタンの変更時にのみinputをポップアップします.
**無条件の非制御部品でよろしいですか?
必ずしもそうとは限らない
非制御構成部品の場合
文章を書くときはぐずぐずするかもしれません(文章が大きすぎると)
また、駆動素子については、100%の状態に保持すると、
文章が間違っている可能性があります(安定性が少し悪い)ので、文字は絶対に間違ってはいけません.例えばカード番号などです.
そのようなものは制御素子として用いることが望ましい.
要するに、非制御素子react-hook-formの使用を試みます
サイト、get start詳細(タイプもあります)
yarn add react-hook-form
後続のインデックスページの作成
import {useForm} from 'react-hook-form'
const{function}=useForm()の使用
例としてHandleSubmitまたはRegisterを使用
import { useForm } from "react-hook-form";

export default function ReactHookFromPage() {
  const { handleSubmit, register } = useForm();

  function onClickLogin(data) {
    console.log(data);
    // loginUser API 요청하기!!
  }

  return (
    <form onSubmit={handleSubmit(onClickLogin)}>
      이메일: <input type="text" {...register("myEmail")} />
      비밀번호: <input type="password" {...register("myPassword")} />
      <button>로그인하기</button>
    </form>
  );
}
次に、ログインボタンに対してonClick関数を実行します.
今はフォームラベルですHandleSubmitの隣にあります
<form onSubmit={handleSubmit(onClickLogin)}>
このように置く
レジスタも使い方によって入れます.

簡単すぎます.😦

検証ライブラリ


yup-リポジトリ
react-hook-form(すなわち練習)
インストール
yarn add yup
index.yupからtsxにインポート
(すべての銀を持ってきて、asの後ろに偽の名前をつけたという意味です)

const schema = yup.object().shape({
  myEmail: yup
    .string()
    .email("이메일 형식이 적합하지 않습니다.")
    .required("반드시 입력해야하는 필수 사항입니다."),

  myPassword: yup
    .string()
    .min(4, "비밀번호는 최소 4자리 이상입니다.")
    .max(15, "비밀번호는 최대 15자리까지 입니다!!")
    .required("비밀번호는 반드시 입력해주세요!!"),
});
.object-ターゲット
.shape
.string-文字列タイプ
.email(「Eメールの形式が正しくありません」)-Eメールの形式です.形式が正しくない場合は、で出力されます.
.required(「パスワードを必ず入力してください!」)-入力を受け入れる必要があります.そうしないと、メッセージが出力されます.
.min(4、「パスワードは少なくとも4桁以上」)-4ビットの最小値、またはメッセージ出力を受け入れる
.max-最大値の指定
react-hook-formに接続します
yarn add @hookform/resolvers
import { yupResolver } from "@hookform/resolvers/yup/dist/yup";
その後、useFormセクションでresolver:yupResolver(schema)を使用してschemaをインポートします.
エラーにformStateが含まれていると判断し、エラーに関するdivを作成します.
<div>{formState.errors.myEmail?.message}</div>
完成本
import { useForm } from "react-hook-form";
import * as yup from "yup";
import yupResolver from "@hookform/resolvers/yup/dist/yup";
// import { yupResolver } from "@hookform/resolvers/yup/dist/yup.js";

const schema = yup.object().shape({
  myEmail: yup
    .string()
    .email("이메일 형식이 적합하지 않습니다.")
    .required("반드시 입력해야하는 필수 사항입니다."),

  myPassword: yup
    .string()
    .min(4, "비밀번호는 최소 4자리 이상입니다.")
    .max(15, "비밀번호는 최대 15자리까지 입니다!!")
    .required("비밀번호는 반드시 입력해주세요!!"),
});

interface FromValues {
  myEmail: string;
  myPassword: string;
}

export default function ReactHookFromPage() {
  const { handleSubmit, register, formState } = useForm({
    mode: "onChange",
    resolver: yupResolver.yupResolver(schema),
  });

  function onClickLogin(data: FromValues) {
    console.log(data);
    // loginUser API 요청하기!!
  }

  return (
    <form onSubmit={handleSubmit(onClickLogin)}>
      이메일: <input type="text" {...register("myEmail")} />
      <div>{formState.errors.myEmail?.message}</div>
      비밀번호: <input type="password" {...register("myPassword")} />
      <div>{formState.errors.myPassword?.message}</div>
      <button>로그인하기</button>
    </form>
  );
}
パスワードが条件を満たしていない場合、メッセージが出力されるのが見えます.
色を変えましょう
ボタン内でisValidをformStateに設定します.isValidにすればいい
import { useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup/dist/yup";
// import { yupResolver } from "@hookform/resolvers/yup/dist/yup.js";
import styled from "@emotion/styled";

interface ImyButtonProps {
  isValid: boolean;
}

const MyButton = styled.button`
  background-color: ${(props: ImyButtonProps) =>
    props.isValid ? "yellow" : ""};
`;

const schema = yup.object().shape({
  myEmail: yup
    .string()
    .email("이메일 형식이 적합하지 않습니다.")
    .required("반드시 입력해야하는 필수 사항입니다."),

  myPassword: yup
    .string()
    .min(4, "비밀번호는 최소 4자리 이상입니다.")
    .max(15, "비밀번호는 최대 15자리까지 입니다!!")
    .required("비밀번호는 반드시 입력해주세요!!"),
});

interface FromValues {
  myEmail: string;
  myPassword: string;
}

export default function ReactHookFromPage() {
  const { handleSubmit, register, formState } = useForm({
    mode: "onChange",
    resolver: yupResolver(schema),
  });

  function onClickLogin(data: FromValues) {
    console.log(data);
    // loginUser API 요청하기!!
  }

  return (
    <form onSubmit={handleSubmit(onClickLogin)}>
      이메일: <input type="text" {...register("myEmail")} />
      <div>{formState.errors.myEmail?.message}</div>
      비밀번호: <input type="password" {...register("myPassword")} />
      <div>{formState.errors.myPassword?.message}</div>
      <MyButton isValid={formState.isValid}>로그인하기</MyButton>
    </form>
  );
}
こんなことをするのは短すぎる
さらにchema、emotion、types、validationなどの素子を除いて30行程度しかありません.
!! button存在ボタンタイプ
これまでは書くだけで関数が実行されていたが、実際にはsubmitタイプがあるため、このように実行された.
  return (
    <form onSubmit={handleSubmit(onClickLogin)}>
      이메일: <input type="text" {...register("myEmail")} />
      <div>{formState.errors.myEmail?.message}</div>
      비밀번호: <input type="password" {...register("myPassword")} />
      <div>{formState.errors.myPassword?.message}</div>
      <MyButton isValid={formState.isValid}>로그인하기</MyButton>
      {/* <button type = "button" onClick={onClickMove}목록으로 이동하기</button> */}
      {/* <button type = "reset">초기화하기</button> */}
    </form>
  );
}
だからbutton type=「button」単独で貼らないとここはsubmitになるので注意

共通構成部品


一度別れたら.
コンテナ/プレゼンテーションだけでなく
Inputまたはbuttonは共通素子に減らすこともできます
typeは道具を出してひっくり返すときに道具を使うこともできますtypeとしてインポートする必要があります.
同じようにregisterも...props.レジスタにインポートする必要があります
これにより、構成部品が再使用され、任意の場所に書き込むことができます.
共有とインポートが問題です
実際、もっとよくやった前任者や上司は、私はいつもimportをします.練習しましょう.
useApolloClient
React-hook-form
リポジトリ