🧯

Angular エンジニアのための React 言い換え表

2021/06/06に公開

最近 Angular を触り始めた React エンジニアです。
ただ、周りでは Angular な人が React を触ることになりそうなので、自分の Angular の勉強も兼ねて Angular -> React の言い換え表を作ってみようと思います。

Props

@Input/@Output

React では input/output の区別は特になく、親から子のコンポーネントに渡すものは全て Props というオブジェクトとして定義されます。

@Output は Angular では子が発するイベントを親が購読している関係になっていると思いますが、React では親から関数を渡して、それを子側でコールバックとして実行するという感じです。

type Props = {
  text: string
  doSomething: () => void
}

const Comp: React.VFC<Props> = props => {
  return <div onClick={props.doSomething}>{props.text}</div>
}

ライフサイクルフック

Angular では ngOnInit などのコンポーネントのライフサイクルに合わせたフックが存在します。

ただ、 React では現在ライフサイクルという概念では動いていません。昔は ComponentDidMount や componentWillUnmount のような形で Angular のライフサイクルとほぼ同じでしたが、今では基本的に useState や useEffect などの hooks で値の変化に反応して処理を実行するようになっています。

https://ja.reactjs.org/docs/hooks-intro.html

なので、一応書きに読み換え表を書きますが、メンタルモデルを変えていただくのが本質的かなとは思います。

useEffect について

useEffect は第一引数に実行したい関数、第二引数に dependency list(正式名称なのかは知らない)と呼ばれる反応したい状態を配列で定義できます。

例えば次のような使い方をすると、state の値が変わる度に console.log が実行されます。

const SomeComponet = () => {
  const [state, setState] = useState("")
  
  useEffect(() => {
    console.log("新しいstateは: ", state)
  }, [state])
  
  return (
    <input value={state} onChange={(e) => setState(e.target.value)} />
  )  
}

ngOnInit, ngAfterViewInit

これは useEffect を用います。
dependency list に何も入れないことによってマウント時の初回のみ実行されるようになります。

useEffect(() => {
  // 何かやる
}, [])

ただ厳密に言うと ngOnInit は描画前に一回だけ実行されるというものだと思うので、それと全く同じなものが欲しい場合は次のように ref というものを使って一回だけ描画前に一回だけ実行されるようにします。

const useComponentWillMount = (func) => {
    const willMount = useRef(true)

    if (willMount.current) func()

    willMount.current = false
}

ちなみに ref についてはこちらも読むと面白いと思います。
https://qiita.com/seya/items/6bbfa3f9d489809ccb2c

ngOnChanges

これは useEffect で代替できます。

useEffect(() => {

}, [ここに反応したい状態を入れてね])

ngOnDestroy

useEffect 内で return した関数はそのコンポーネントが unmount される時に実行されます。

useEffect(() => {
 return () => console.log("unmount された!")
}, [])

テンプレート部分の書き方

Angular で言うテンプレート(.html) での細かい書き方

ngIf

自分は && 演算子を使って書くことが多いです。

return (
  <div>
    {props.title !== null && <h1>{props.title}</h1>}
  </div>
)

ngFor

map などの関数を用いて書くことが多いです。

return (
  <ul>
    {props.items.map(item => <li key={item.id}>{item.title}</li>)}
  </ul>
)

ng-content

子供のコンポーネントを受け取る方法ですが、 props に { children: React.ReactNode } があれば大丈夫です。

type Props = {
  children: React.ReactNode
}

const Child: React.VFC<Props> = props => {
  return <div>{props.children}</div>
}

// 使う側
const Oya: React.VFC = () => {
  return <Child><p>これが上記の children のところに表示されます。</p></Child>
}

trackBy

繰り返し要素を描画する時に不要な際描画を防ぐために使われる trackBy ですが、こちらは React では key で表現されます。
key にはリストのアイテム毎に一意な値を渡します。大抵はオブジェクトの ID などが好ましいです。

return (
  <ul>
    {props.items.map(item => <li key={item.id}>{item.title}</li>)}
  </ul>
)

https://ja.reactjs.org/docs/lists-and-keys.html

ng-container

React では React.Fragment というものを使います。

<React.Fragment>
  hoge
</React.Fragment>

また、この React.Fragment は次のように省略して書くこともできます。ただし、key を渡す場合は上記のように省略しないで書かなければいけません。

<>hoge</>

その他

ChangeDetectionStrategy.OnPush

useMemo, useCallback, React.memo でググってみてね!
useMemo と useCallback については昔記事を書きました。
https://qiita.com/seya/items/8291f53576097fc1c52a

Discussion