LoginSignup
10
6

More than 5 years have passed since last update.

React + Unduxでみんなを幸せにする

Posted at

ほんとにめっちゃ使いやすいので早く使ってください

いつまでReduxでヒイヒイ言ってるんですか?

Unduxって?

Reactの状態管理をするため(バケツリレーとかしたくない)のライブラリ
公式:undux

特徴

  • ただ使うだけならgetとsetしか存在しない優しい世界
  • ただし、rxjsを利用すれば複雑なこともできる
  • (僕は)基本的にReactの各ComponentのStateで状態管理するという概念にあっている(と思う)

とりあえずコードを見てくれ

今回つくるのは簡単なチャットアプリのモックだと思ってください

チャットアプリの全ソース:socket_test

まずunduxのインストールから

npm install --save rxjs undux

そして使い方は簡単
まずは保存したい値を入れるStoreを作る

Store.js
import { createConnectedStore } from 'undux'

var store = createConnectedStore({
    comments: [],
    user: {
        name: null
    }
});

export default store;

次にStoreをpropに渡すためにルートのコンポーネントを囲う
(今回はルーターにreact-routerを使ってます)

index.jsx
import React from "react";
import ReactDom from "react-dom";
import { BrowserRouter } from "react-router-dom";
import Store from "./Store";

//router
import App from "./App";

class Root extends React.Component {
    render() {
        return (
            <Store.Container>
                <BrowserRouter>
                    <App />
                </BrowserRouter>
            </Store.Container>
        );
    }
}

ReactDom.render(<Root />, document.getElementById("root"));

そしたらStoreを呼び出したいコンポーネントをwithStoreでラップ

Chat.jsx
import React from "react";
import Store from "../Store";

class Chat extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            msg: "",
            comments: []
        }
    }

    componentDidMount() {

    }

    changeTxt(e) {
        this.setState({ msg: e.target.value });
    }

    render() {
        var messages = this.state.comments.map((comment, index) => {
            return <p key={index}>{comment.msg}</p>
        });
        return (
            <div>
                <input type="text" value={this.state.msg} onChange={(e) => {
                    this.changeTxt(e);
                }} />
                <button onClick={() => {
                    actions.sendMsg(this.props.store, this.state.msg);
                    this.setState({ msg: "" });
                }}>
                    投稿
                </button>
                <div>
                    {messages}
                </div>
            </div>
        );
    }

}

export default Store.withStore(Chat);

Storeの値はthis.props.storeから.get("key")すればとれますが、このまま使うと値の更新時にレンダリングしてくれなくなってしまうのでComponentWillMount内で監視して、値自体はstateのものを使います。

Chat.js
    componentDidMount() {
        var store = this.props.store;

        //stateにstoreの値をbind
        store.on("comments").subscribe((comments) => {
            this.setState({ comments: comments });
        });
    }

rxjs知らない人のために補足。
クリックイベントなどと同様に、onで値の変更を監視して、subscribeで変更があった時の挙動の制御をします。

後は各々好きなところで値を受け取ってセットすればいい。
僕は個人的にActionファイルは作りたい派なので下記のような構造になった。(Socketわからない人ごめんなさい、普通にComponentWillMount内でAPIにデータ取りに行ってると思ってもらって構いません。)

Chat.jsx
   componentDidMount() {
        var store = this.props.store;

        socket.on('entered', (data) => {
            var comments = store.get("comments");
            comments.push(data);
            store.set("comments")(comments);
        });

        socket.on('received', (data) => {
            var comments = store.get("comments");
            comments.push(data);
            store.set("comments")(comments);
        });

        socket.on('leaved', (data) => {
            var comments = store.get("comments");
            comments.push(data);
            store.set("comments")(comments);
        });

        //stateにstoreの値をbind
        store.on("comments").subscribe((comments) => {
            this.setState({ comments: comments });
        });
    }

action/chat.jsx
import socket from "../socket";

export const entry = (store, name) => {
    var user = store.get("user");
    user.name = name;
    store.set("user")(user);
    const data = { name: name };
    socket.emit('connected', data);
}

export const sendMsg = (store, msg) => {
    const name = store.get("user").name;
    const data = {
        name: name,
        msg: msg
    };
    var comments = store.get("comments");
    comments.push({ msg: name + " : " + msg });
    store.set("comments")(comments);
    socket.emit('sendMsg', data);
}

ちなみにプロダクト作る際はもっと複雑にStore使うことになると思うのですが、その際はNamedStore使うと便利です。

namedStore.js
import { createConnectedStoreAs } from "undux";

const initialA = {
  aHoge:null,
  aFuga:null
}

const initialB = {
  bHoge:null,
  bFuga:null
}

const store = createConnectedStoreAs({
  A: initialA,
  B: initialB
});

export default store;

こんな形で書いてあげれば、Store.withStoresで囲ってあげればそれぞれthis.props.A , this.props.Bといった形でそれぞれのstoreが下りてきます。
※公式のExampleにはwithStoreって書いてあるので注意。PR送ったのでそのうち治るはず。

よかったこと、悪かったこと

正直、とても使いやすくて、

  • 学習コストなんてない(rxjsの勉強はしたほうがいいかも)
  • 値の監視
  • state内でちゃんと管理できる
  • 使うのにミドルウェアいらない
  • onとsubscribe使えばvue.jsとかmobxみたいなComputedも実現できる(unduxではEffectって言う)
  • ライブラリ内部のコードが見やすい(大体定義にジャンプすれば解決する)

といったあたりがほんとに好きでした。

悪かったことは

  • 公式リファレンスがもう少し充実するといい
  • まだ出たばかりなので資料が少ない(それでも全然困らないレベルなのはほんとにシンプル目指してるだけあるすごい)
  • 値の監視の部分が少し冗長な感じはする

といったあたりでしょうか?

個人的にはstateの値を使ってきれいなReactが書けるので気に入っています。
Reduxとかだと自動でレンダリング走らせてくれたりするので、propsの値を直で使ったりしがちなのが今まではまりやすいポイントだったのかなと思ったので。
あとは、Storeには共通の値を入れといて各コンポーネントで加工して使うとかやりやすそう。

みんなもundux使ってハッピーになりましょう。

10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6