JSX で HTML のように属性値を渡すとkeyref(React が扱う値)以外はpropsとして子コンポーネントに渡せます。

です。

const Child = props => {
  return 

{props.text || "default text"}

; }; const App = () => { return (
); };

Childtext属性を使っているので、親が使うときにtext属性を置いてあげるとその値を使うことができます。ですが、この値はが必ず渡されるという保証はありません。
予期しない動作にならないように、子ではフォールバック値が使われるように|| "default text"のように設定しておくと安心です。

公式ドキュメントには、「頭に小文字を使うと HTML かと思うので、大文字で定義してください」というようなことがこの辺に書かれていますが、少し分からないケースがでてきたので自分でもトランスパイルしてみて調べました。

変数

この辺はドキュメントの通り頭小文字で定義した場合は単に文字列として渡されていました。

const orange = () => 
; const Orange = () =>
;

展開後。

React.createElement('orange', {
  style: {padding: 10, background: 'orange'}
});
React.createElement(Orange, {
  style: {padding: 10, background: 'orange'}
});

オブジェクト

プロパティアクセス

ここが良く分からなかった部分です。
オブジェクトの場合は、大文字小文字関係なく React コンポーネントとして渡されます。

const div = {}
div.orange = () => 
; div.Orange = () =>
;

展開後。

React.createElement(div.oarnge, {
  style: {padding: 10, background: 'orange'}
});
React.createElement(div.Orange, {
  style: {padding: 10, background: 'orange'}
});

ちなみにこの場合にdivを使っても React はそれを単に HTML タグと考えらるので問題ないです。

// 上の続き
// は React.createElement('div', null);

インデックス記法でのアクセス

これもまぁドキュメントに書かれているのですが、div['orange']というような形を JSX のタグで使うことはできません。その場合は事前にOrangeのような変数にその値を格納してから<Orange />のように使う必要があります。

コンポーネント内部の状態を持つ為に使います。この関数は初期値を引数として渡せ、現在値と更新用の関数を返します。

以下は例です。

const [hidden, setHidden] = useState(false);

hidden値とsetHidden関数を得ます。単にこれは分割代入なので名前は適当に決めます。

定義した直後hiddenは引数として渡したfalseが入ります。この値をtrueにするにはsetHidden関数に次の値を渡して実行します。

setHidden(true);

更新用の関数は、引数に関数を取ることもできます。引数の関数には現在値が渡されて実行され、戻り値が次の現在値になります。なので、以下のようにも書けます。

setHidden(state => !state)

値を更新すると、コンポーネントの状態を更新したという事になるので、このフックを持つコンポーネントは再描画されます。

コンポーネントを Flux フロで管理したいならuseStateの代わりに使うのがいいかもしれません。特に管理する状態が多い場合はこちらを使うと処理を外部に切り分けられコードが見やすくなるのでお勧めします。

このフックは2-3の引数を受け取ります。1つ目は Reducer、2つ目はその Reducer の初期値、3つ目はオプショナルで2つ目の初期値が複雑な場合、それを更に弄る為の関数です。このフックは戻り位置に状態statedispatchという Reducer へアクションを流すための関数を返します。

です。

const init = initialState => {
  initialState.count = Math.floor(Math.random() * 10);

  return initialState;
};

const INCREMENT = "INCREMENT";
const DECREMENT = "DECREMENT";

const initialCountState = { count: 0 };

const countReducer = (state, action) => {
  switch (action.type) {
    case INCREMENT: {
      const nextState = { ...state };
      nextState.count++;
      return nextState;
    }

    case DECREMENT: {
      const nextState = { ...state };
      nextState.count--;
      return nextState;
    }

    default: {
      return state;
    }
  }
};

const App = () => {
  const [state, dispatch] = useReducer(countReducer, initialCountState, init);

  const increment = useCallback(() => {
    dispatch({
      type: INCREMENT
    });
  }, [dispatch]);

  const decrement = useCallback(() => {
    dispatch({
      type: DECREMENT
    });
  }, [dispatch]);

  return (
    
{state.count}
); };

useReducerには上記の説明のように(ここでは無理やり)3つの引数を渡してます。initialStateではcount0としてますが、init関数により0から9のどれかに書き換えられます。

アクションを発行する関数はuseCallbackで囲みます。この Reducer を使った更新ではdispatch関数は毎回変更が入ってしまい毎回アクション関数を新しく作ることになってしまいますが、依存しないpropsの更新が来た時などにはそれを防げる為です。今回はpropsがありませんが、そういった場合でも癖として常にやっておいたほうがいいかなと思います。

<button>をクリックするとcountReducerへアクションが飛びます。countReducerではそのアクションのtypeを見て捌きます。switch構文で捌くことが多いですが、if構文などでも構いません。

そしてcountReducerの戻り値が次のuseReducerの戻り値のstateへやってきます。そして、その値でコンポーネントが再描画される…という動作を繰り返します。

条件が同じ時に同じ関数を返すことができる関数です。1つ目の引数にある条件の時の関数、2つ目の引数に条件を渡します。戻り値もまた関数でその引数や戻り値は1つ目の引数に渡した関数と同じになります。

以下は例です。

const fn = useCallback(
  () => {
    console.log('called with: ', foo, bar, baz);
  },
  [foo, bar, baz]
);

まず前提として JavaScript では(() => {}) !== (() => {})です。これは参照先が違う為です。

上記コードのfnfoobarbazに依存してます。例えば

  1. foo === 1

  2. bar === 2

  3. baz === 3

の時、関数は'called with: 1 2 3'表示する関数を返します。さて、このような動作の関数は何個も必要なのか。やってることは同じなので1つ'called with: 1 2 3'表示する関数を作れば十分なはずです。それを1つにしちゃうのがuseCallbackです。

つまり、useCallbackがやっているのはfoobarbazがセットで同じ場合、前回作った関数があればそれを使うという事をするフックという事になります。

依存無しの関数

もし関数の内部が外部のどんな変数にも依存してない場合、明らかに生成が必要な関数は1つになります。明らかに1つの時は条件に空配列を置きます。

useCallback(
  () => { /* ... */ },
  []
);

これで常に最初に渡した() => { /* ... */ }が返ります。

依存無しなのにわざわざuseCallbackを使う必要があるのかとも思うかもしれません。これは必要あります。でなければ、子コンポーネントに渡すときに違う参照の関数を渡すことになり、子コンポーネントから見れば毎回違うセットのpropsが渡ってくるように見える為です。

ほぼuseCallbackと同じ役割です。主に参照を返すような値をいくつも作らないようにする為に使います。

useMemoには1つ目の引数に好きな値、2つ目にはuseCallbackなどと同じ用に条件を渡します。この条件がセットで同じ値の時は前回返した値を返します。

以下は例です。

const obj = useMemo(
  {foo, bar, baz},
  [foo, bar, baz]
);

例えば、

  1. foo === 1

  2. bar === 2

  3. baz === 3

な時obj{foo: 1, bar: 2, baz: 3}というオブジェクトを返します。次に同じ条件のセットで再度実行されるとobj前回返した{foo: 1, bar: 2, baz: 3}をそのまま返します。もし違う条件のセットであれば新しいオブジェクトを作って返します。

依存無しの値

もし値が外部のどんな変数にも依存してない場合、明らかに生成が必要な値は1つになります。明らかに1つの時は条件に空配列を置きます。

useMemo(
  { /* ... */ },
  []
);

これで常に{ /* ... */ } === { /* ... */ }な値が返ります。

依存無しなのにわざわざuseMemoを使う必要があるのかとも思うかもしれません。これは必要あります。でなければ、子コンポーネントに渡すときに違う参照の値を渡すことになり、子コンポーネントから見れば毎回違うセットのpropsが渡ってくるように見える為です。

コンポーネントのライフサイクル時に処理を行うために使います。ライフサイクルとはコンポーネントが、

  1. レンダリングされた時

  2. リレンダリング(更新)された時

  3. 削除される時

です。

useEffectは引数に2つの値を渡します。1つ目は上記のライフサイクル処理を行う関数、2つ目はライフサイクル時に関数を発火するかどうかの依存条件をuseCallbackらと同じように渡します。戻り値はありません。

以下は例です。

const [foo] = useState('');

useEffect(
  () => {
    console.log(foo);
  }, [foo]
);

useEffectはレンダリング後に1回は必ず呼ばれます。その後は引数の条件によって発火するかが変わります。このコードではfooが依存条件としてセットされているのでfoo更新された後にも発火されます。

ライフサイクルの1と2は出てきましたが3はどう使うのか。実は、上記のコードではこれは実装されていません。3を行うには1つ目の引数の関数を修正する必要があります。それは、関数の戻り値として関数を返す事です。

少し書き換えます。

const [foo] = useState('');

useEffect(
  () => {
    console.log(foo);
    
    return () => {
      console.log('unmount');
    }
  }, [foo]
);

これでこのコンポーネントが削除される際にunmountとコンソールに表示されるはずです。

2以外を実装

つまり更新時の処理はいらないを実現する方法です。これは依存条件に空配列を渡すだけで実装できます。
「最初の1回は必ず実行され…最後にも1回…」

useEffect(
  () => () => { /* ... */ }
  []
);

ここから3の削除時のライフサイクルもいらない場合はどうするか。関数を返さなければいいですね。

useEffect(
  () => { /* ... */ }
  []
);

useLayoutEffect

ちなみにこちらの似たフックは、レンダリングされて反映される直前に実行されるライフサイクルです。ライフサイクルの順番が早いのはuseLayoutEffect > useEffectです。

コンポーネントで返した HTML 要素を使う時に使います。HTML 要素へ渡す場合、基本的に初期値はnullを渡します。

const buttonRef = useRef(null);

要素はbuttonRef.currentに入りますが、コンポーネントがレンダリングされるまではその値はnullです。レンダリング後は狙った要素の参照が入ります。

準備

buttonRef.currentにほしい要素のref属性にこのbuttonRefを渡してあげます。

useRefと同じ様な使用用途です。useRefはコンポーネント内の HTML 要素を触る為のフックでしたが、useImperativeHandle親コンポーネントにコンポーネント内の要素を触る為の手段を提供する為のフックです。React.forwardRefとの違いは2つほどあると思います。

  1. コンポーネント内の要素を親コンポーネントから直接触られない

  2. コンポーネント内の複数の要素(参照)を親コンポーネントは触ることができる

例。このフックは親から参照をもらうためにforwardRefと共に使います。

const Checkbox = forwardRef((_, ref) => {
  const checkboxRef = useRef(null);

  useImperativeHandle(ref, () => ({
    check() {
      if (checkboxRef.current !== null) {
        checkboxRef.current.checked = true;
      }
    }
  }));

  return ;
});

こうして渡ってきた親コンポーネントからのref.currentに対して、useImperativeHandleハンドラーを生やします。この例ではcheckメソッドを持つオブジェクトを返していますが、これがまさにref.current.checkを実装していることになります。
この中のcheckboxRefは要素の為のuseRefなので普段通りに使えます。

親コンポーネントからref.current.check()が呼ばれるとコンポーネント内のinput[type=checkbox]のチェックフラグをtrueに書き換えます。(React にはもっと優れた方法があるので実際にはこのように実装しないように)

親コンポーネント

親です。親では何度も言っているref.current.check()の実行の仕組みとハンドラーを生やすためのrefforwardRefなコンポーネントに渡す必要があります。

export default () => {
  const ref = React.useRef(null);
  const onClick = React.useCallback(() => {
    ref.current.check();
  }, []);

  return (
    
); };

forwardRefに渡すuseRefも基本的にnullでフォールバック値を与えます。これは子要素が存在しない場合はref.currentにはnullが入るようにする為です。

check!ボタンを押してinput[type=checkbox]にチェックが入るか確認できたら完了です。

ユースケース

Pulldown 実装

タブ移動をサポートしないといけないようなアプリケーションの時focus要素の操作にuseImperativeHandleは便利かもしれません。これは実装確認です。

この要素は、Pulldown にフォーカスが入った後サジェストが開き、その後サジェストの最後の要素が抜けるまでその状態を維持し、抜けたら Pulldown を閉じるというような動作が期待されます。

サジェストの要素どれかにフォーカスがあたってるかをuseImperativeHandleを使って親から調べれるようにすると、親要素である Pulldown の開く・閉じるタイミングが簡単に制御できるようになります。また子の参照だけではなく上の方で書いた Checkbox のように DOM を操作することもできるので、例のように子がそういうハンドラーを提供すれば、親から Focus している要素を変更するといった処理も行えます。

子コンポーネントの適当なものと紐付いた参照を親コンポーネントから覗きたい場合に使います。React.forwardRefでラップするコンポーネントはpropsの他にrefを2つ目の引数に受け取ります。この中身は親コンポーネントのどこかで生成された参照です。

以下は1秒後にチェックボックスをチェックするです。

const Child = React.forwardRef((_, ref) => {
  return ;
});

const App = () => {
  const ref = React.useRef(null);

  React.useEffect(() => {
    setTimeout(() => {
      ref.current.checked = true;
    }, 1000);
  }, []);

  return ;
}

Childに対してReact.forwardRefを使ってます。そして渡されたrefinput[type=checkbox]に渡すことでこの要素を親から直接触れるようにしてます。

Appでは描画が完了後にそのrefを通してinput[type=checkbox]checkedを真にすることでチェック済の状態に DOM を更新してます。

JavaScript で飯食べたい歴約 8 年、 純( nju33 ) によるノートサイトです。

このサイトではドリンク代や奨学金返済の為、広告などを貼らせて頂いてますがご了承ください。

Change Log