強まっていこう

あっちゃこっちゃへ強まっていくためのブログです。

Redux と React hooks どっち使ったら幸せになれるの?

結論としては React を使わない事が一番の幸せへの道。

React ファンボーイはそっ閉じの方向で、以下読むべからず。

簡単なものを作るのにもアホほど時間がかかるので膨大な予算が無駄に取れる贅沢プロジェクトで暇つぶししたい場合のみに使うと良い。

表示速度を上げるにもアホほど労力と精神力と工数を吸い取られ続けるし、大人数でやればやるほどダメになり表示速度と開発速度が共に劇的に遅くなって行く。

そもそもこんなもの Facebook みたいに1画面で多人数から飛んでくるデータをビシバシ表示していろんなやり取りするものの為に作られたのであって、そんなイカツイウェブアプリを作らなきゃならない状況が一般企業にどれほどあるか?そしてそんな無駄な金をドバドバ出せるところがどれほどあるか?って話。GAFA の F だよ?やつらは。

予算かけずさっさと動くものを作れ、バグ出すな、表示速度上げろ、とか簡単に言い出す連中が大半を占める中、使っちゃ駄目なんだってこんなもん。理想と現実をちゃんと見るべし。

やむなく涙ながらにどちらかを使わざるを得ない場合はしょうがなく React hooks を勧める。

wolfbash.hateblo.jp

上のエントリーで貼り付けたコードと、以下に示す React hooks のコードを見比べて欲しい。

import axios from 'axios'
import React, { useReducer } from 'react'

const gStatus = {
  value: 'AAA',
  time: '---',
}
const gReducer = {}
const gDispatcher = {}
const gAsyncDispatcher = {}

gReducer.send = (state, value) => {
  return { ...state, value }
}
gReducer.hoge = (state) => {
  return { ...state, value: 'HOGE' }
}
gReducer.getTime = (state, time) => {
  return { ...state, time }
}

gDispatcher.send = args => args
gDispatcher.hoge = () => {}
gAsyncDispatcher.getTime = async (arg) => {
  const res = await axios.get('https://ntp-a1.nict.go.jp/cgi-bin/time')
  return arg + res.data
}

const Main = props => {
  const [ state, dispatch ] = makeStateAndDispatch(gReducer, gStatus, gDispatcher, gAsyncDispatcher)
  return (<div>
    <Input state={state} dispatch={dispatch} />
    <Display state={state} dispatch={dispatch} />
  </div>)
}

const Display = ({ state, dispatch }) => {
  return (<div>
    <p>Display: {state.value}</p>
    <p>Time: {state.time}</p>
  </div>)
}

const Input = ({ state, dispatch }) => {
  let input = ''
  return (<div>
    <input type='text' ref={ref => (input = ref)} defaultValue='' />
    <button onClick={() => { dispatch('send', input.value); input.value = '' }}>Send</button>
    <button onClick={() => dispatch('getTime', 'NOW=')}>getTime</button>
    <button onClick={() => dispatch('hoge')}>Hoge</button>
  </div>)
}

export default (props) => {
  return <Main />
}

// 以下本来外に出すべき関数(記事の便宜上ここにおいている)
const makeStateAndDispatch = (reducer, status, dispatcher, asyncDispatcher = {}) => {
  let [ state, dispatch ] = useReducer((state, action) => {
    if (reducer[action.type]) {
      return reducer[action.type](state, action.payload)
    }
    else { return state }
  }, gStatus)
  const dispatchCaller = async (type, arg) => {
    let payload = null
    if (dispatcher[type]) {
      payload = dispatcher[type](arg)
    }
    else if (asyncDispatcher[type]) {
      payload = await asyncDispatcher[type](arg)
    }
    dispatch({ type, payload })
  }
  return [ state, dispatchCaller ]
}

どうだろう。簡単に書けているだろうか。

あんまし変わってなくね?と思ったなら正解。

そう大して変わらない。便利関数を用意してしまえば違いはあまり無いのだ。

ただ Hooks には色々便利なやつがいて、こいつを使いこなすと楽が出来たりする。

特に Form を扱う場合、React Hook Form を使うのと Formik だの Redux-Form を使うのとでは、可読性、レンダリング速度等々雲泥の差があるので Hooks を使うべきだ。

しかし、useMemo、useMemo、React.memo 等を駆使しないとレンダリングが無駄に走るので本気でやるとかなり神経を使う。

この呪いは Redux 使っても巻き起こる問題なので、React 自体使うことをオススメしない。

<div>
  <input id='input'>
  <button id='send'>Send</button>
  <button id='getTime'>getTime</button>
  <button id='hoge'>Hoge</button>
</div>
<p>Display: <span id='value'></span></p>
<p>Time: <span id='time'></span></p>
<script>
  const gStatus = {
    value: 'XXX',
    time: '---',
  }
  for (var k in gStatus) {
    $(`#${k}`).html(gStatus[k])
  }
  $('#send').click(() => {
    const val = $('#input').val()
    $('#input').val('')
    $('#value').html(val)
  })
  $('#hoge').click(() => {
    $('#value').html('HOGE')
  })
  $('#getTime').click(async () => {
    const res = await axios.get('https://ntp-a1.nict.go.jp/cgi-bin/time')
    $('#time').html(`NOW=${res.data}`)
  })
</script>

これで OK な場合はこれでOKなんだよ。シンプルなものを使って作る。変に格好つけなくて良し。それが幸せへの近道。