Open14

typescriptネタ

イネダイネダ

カスタムフックのパターン

  • カウンタータイプ
    • 操作を公開する、任意のイベント、コールバックを渡して更新させる
  • APIタイプ
    • 操作公開しない、内部で完結させる
    • useEffect内に各理由は、基本的にコンポーネントもカスタムフックも トップレベルの関数は、stateが変化すると、呼び出されてしまう。これを防ぐために初回のみ何かしたい場合に、useEffectを使用する
イネダイネダ

stateが紐づけられていると、何か変化があった場合にコンポーネント内のロジックは毎回実行される

イネダイネダ

Refを使って子要素のフォームをクリックしたことにする

function ParentComponent() {
  const myRef = useRef<HTMLFormElement>(null);

  return (
    <>
      <p>子要素のフォームを呼び出し</p>
      <button
        onClick={() => {
          myRef.current?.submit();
        }}
      >
        call ref
      </button>
      <ChildComponent refObject={myRef} />
    </>
  );
}

type ChildComponentProps = {
  refObject: RefObject<HTMLFormElement>;
};

//
function ChildComponent(props: ChildComponentProps) {
  return (
    <>
      <Form ref={props.refObject} method="GET" action="/hoge"></Form>
    </>
  );
}
イネダイネダ

forwardRefは何か?
どうやらrefはカスタムコンポーネント(関数型には)つかえないようだ。
カスタムコンポーネントにはfowardrefを用いるみたい。

イネダイネダ

プロパティ:レンダー関数

〈プロパティ〉というのが理解できてなかった

イネダイネダ
type RefPageProps = {
  ref: RefObject<HTMLInputElement>;
};

type RefPageProps = {
  inputRef: RefObject<HTMLInputElement>;
};

既存のrefプロパティと認識されておかしくなっていた。
refプロパティは全てのコンポーネントに最初からあるものと認識する。
全てのコンポーネント?にはrefというフックが付いているイメージ

イネダイネダ
import React, { useRef, RefObject } from "react";
import { Form } from "react-router-dom";

function RefPage() {
  return (
    <>
      <Parent />
    </>
  );
}

function Parent() {
  const formRef = useRef<HTMLFormElement>(null);
  const onClick = () => {
    formRef.current?.submit();
  };
  return (
    <>
      <p>parent</p>
      <button onClick={onClick}>子要素Submit</button>
      <Child formRef={formRef} />
    </>
  );
}

type RefPageProps = {
  formRef: RefObject<HTMLFormElement>;
};

function Child(props: RefPageProps) {
  return (
    <>
      <form ref={props.formRef}>
        <input name={"value1"} />
      </form>
    </>
  );
}

export default RefPage;

サンプル更新

イネダイネダ

inputのvalueに値を設定すると、onChangeを設定しろと警告がでる。
valueを設定すると固定になってしまうようだ。

イネダイネダ
import React, { useRef, RefObject, forwardRef } from "react";
import { Form } from "react-router-dom";

// 参照可能な独自コンポーネント(例フォーム)
const FormRef = (props: any, ref: any) => {
  // onChangeはエラー防止のため
  // 渡されたRefをformに紐づけ
  return (
    <>
      <form ref={ref} {...props}>
        <input name={"key"} value="abc" onChange={() => {}} />
      </form>
    </>
  );
};

// 独自コンポーネント参照可能にするためラップ
const FowardForm = forwardRef(FormRef);

function FowardRefPage() {
  const ref = useRef<HTMLFormElement>(null);
  const onClick = () => {
    ref.current?.submit(); // ref操作
  };

  return (
    <>
      <FowardForm ref={ref}></FowardForm>
      <button type="button" onClick={onClick}>
        ref submit
      </button>
    </>
  );
}

export default FowardRefPage;
イネダイネダ

reactのFowardRefの型定義を見る。
関数型コンポーネントの場合はFowardRefの型が省略されているので分かりづらい。
・refとpropsの引数を持ち、ReactNode(コンポーネント)を返せばよいこと。
・ただし、propsはデフォルト引数が設定されているので考えなくて良い。

  function forwardRef<T, P = {}>(
        render: ForwardRefRenderFunction<T, P>,
    ): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;


interface ForwardRefRenderFunction<T, P = {}> {
        (props: P, ref: ForwardedRef<T>): ReactNode;
        displayName?: string | undefined;
        defaultProps?: never | undefined;
        propTypes?: never | undefined;
    }