旧gaaamiiのブログ

間違ったことを書いている時があります。コメントやTwitter、ブコメなどでご指摘ください

今さらReduxのSSOT原則とか状態管理について考える

Reduxを使ってウェブアプリケーションの開発をしていて、割と早い段階で、どうしてもReactのsetStateを呼びたい場面に遭遇しました。しかしReduxといえば、Single Source of Truthという原則があります。状態はすべて、Redux側で一つの状態ツリーで管理するものだと思っていました。

とすると、Reactコンポーネントたちは状態をpropsとして受け取って描画するだけ、ただのビューの関数であり、Reduxでの状態管理とReactでの要素表示の役割がしっかり分けられてきれいな世界になる...!!これがいまどきのきれいなウェッブアプリケーションなんじゃ!!!実装を始める前まではそんなものを想像していました。

ところが、実装を始めるとすぐにReactのsetStateを呼びたくなりました。それはもうすぐに呼びたくなりました。何かと言うと、いわゆるドロップダウンメニューの開閉表示状態です。今私が利用しているはてなブログの編集画面の、ここの部分みたいなものです。

クリックしたら開いたり閉じたりします。これ系の挙動をするコンポーネントは、だいたい何を作るにしても必要なものだと思います。

要は、この開閉状態を、Reduxで管理せず、Reactのそのコンポーネントのstateに持たせたくなったのです。

Reduxで管理する場合、きっと以下のようなコードが必要になります。

// components/DropdownComponent.jsx
const mapStateToProps = ...
const mapDispatchToProps = ...
export default connect(mapStateToProps, mapDispatchToProps)(DropdownComponent)

// actions/ToggleDropdown.js
export default {
  type: 'TOGGLE_DROPDOWN'
}
// reducers/toggleDropdown.js
const toggleDropdown = (state, action) => {
  switch(action.type) {
    case 'TOGGLE_DROPDOWN' :
    default:
      return !state
  }
}
export default toggleDropdown 

ふむ...ずいぶんと多くのファイルを編集しないといけない。

対して、ReactのsetStateを使う場合。

export default class DropdownComponent {
  constructor(props) {
    super(props)
    this.state = {
      visible: false
    }
  }

  handleClick = (e) => {
    this.setState({ visible: !this.state.visible })
  }

  render() {
    <div>
      <button onClick={this.handleClick}>
      {this.renderContent()}
    </div>
  }

  renderContent() {
    if (this.state.visible) { 
      return <DropdownContent />
    } else {
      return null
    }
  }
}

こりゃいいや!!!このコンポーネントのなかで完結していてわかりやすい!

しかし、そんなことをして怒られないのでしょうか。この軽はずみな実装によってReduxの怒りを買ってしまいプロダクトが破綻してしまっては困ります。

調べてみると、ちゃんとReduxにこんなFAQがありました。

(質問)

Do I have to put all my state into Redux? Should I ever use React's setState()?

まさしく自分が知りたいことです。setState呼んでいいのでしょうか?

(回答)

There is no “right” answer for this. Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state.

なるほど、、全部Reduxで管理するやつもいるし、ドロップダウンの開閉状態みたいなものに関してはコンポーネントに持たせる人もいるよね、と。答えになってないじゃないか!

Using local component state is fine. As a developer, it is your job to determine what kinds of state make up your application, and where each piece of state should live. Find a balance that works for you, and go with it.

それはお前が考えることだ、と。ふぉ〜〜〜。そうですよね〜〜。

改めて考えてみると、もともとはReduxなんかなくてもアプリケーションは作れる。じゃあなんでRedux使うのかというと、コンポーネントの中ではなくアプリケーション全体で持ち回したい状態を一箇所でちゃんと管理したいから、ということになる。それだってRedux使わなくてもできるわけだけど、そうするとバケツリレーと呼ばれるあの、状態とコールバックを受け渡し受け渡しする見通しの悪い地獄みたいなコードを書かないといけなくなる。それは嫌だ。それだったらReduxを使う、となります。

まとめ

ReduxのSingle Source of Truthを厳密に守ろうとするのはきついと感じることもあり、しかしReduxみたい状態管理ライブラリは何かしら必要です。自分の精進が足りず、まだベストな方法を見つけられていない感がありますが、Reduxを完璧なフレームワークと思い込まず、問題があったらそれについて考えながら使いこなしていければと思います。