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);
useLazyQueryuserQueryは、ページの最初に自動的に要求され、撮影されます.
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
リポジトリ
Reference
この問題について(Day 24), 我々は、より多くの情報をここで見つけました https://velog.io/@john_with_smile/Day-24テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol