質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.40%

  • React.js

    961questions

    Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

  • Redux

    133questions

React+Reduxで、Reduxと紐付いたComponentを複数箇所で使う場合の書き方がわからない

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 565

masaya_ohashi

score 8793

 前提・実現したいこと

例えばclickerという名前のReducerと、store上のclickerのデータを参照するClickerというComponentがあるとします。
このClickerというComponentを複数箇所で使う場合、storeの中にはclickerという名前のものが1つしかないのですが、それぞれの使用箇所で独立した状態で管理するにはどのように書くのが一般的なのでしょうか?そもそもこのように汎用的なComponentの内部情報をReduxで管理するのが間違いなのでしょうか?

 発生している問題・エラーメッセージ

別々のデータをstoreで扱いたいが、共通になってしまう。

 該当のソースコード

// actions/clicker.js
function clickerClick() {
  return {type:'clicker.click'};
}
const ClickerActions = {
  clickerClick
};
export default ClickerActions;
// reducers/clicker.js
const initialState = {value:0}
function clicker(state=initialState, action) {
  switch(action.type) {
    case 'clicker.click':
      let ret = Object.assign({}, state);
      ret.value++;
      return ret;
  }
  return state;
}
// reducers/main.js
const mainApp = combineReducers({
    main,
    clicker, // ←reducers/clicker.js
});

export default mainApp;
// components/Clicker.js
class Clicker extends Component {
  render() {
    return <div onClick={e=>this.props.actions.clickerClick()}>{this.props.clicker.value}</div>;
  }
}
function mapStateToProps(state) {
  return {clicker:state.clicker};
}
function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(Object.assign({}, ClickerActions), dispatch)
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(Clicker);

この状態で<Clicker>を複数箇所に使用したとき、storeにはclickerという入れ物は1つしかないので相互に影響してしまうため、独立したデータで持ちたい。

class Hoge extends Component {
  render() {
    return (
      <div>
        <div>
          <h1>Clicker 1</h1>
          <Clicker/>
        </div>
        <div>
          <h1>Clicker 2</h1>
          <Clicker/>
        </div>
      <div>;
  }
}


このように複数箇所で使用した場合、Clicker 1とClicker 2で独立したデータを持てるようにしたいです。Clicker 1で発生したactionはClicker 1にのみ影響を与えるようにしたいです。いまのままだとClicker 1が書き換わるとClicker 2も同時に書き換わってしまいます。そもそもreducerがclickerという共通のものを使ってしまう以上、store内に保存されるデータを別々にすることは出来ないのでしょうか?

 試したこと

いろいろ調べようとしているのですが、どういう用語で調べたものかわからず、なかなか情報に行き当たりません…

【追記】
この方も同じような悩みを抱えているようです。しかしスマートな解決法は見つかっていないようです…
https://qiita.com/derui@github/items/8272c5cb7eb7fc3ecd67

 補足情報(FW/ツールのバージョンなど)

package.json一部抜粋

    "react": "^16.2.0",
    "react-redux": "^5.0.7",
    "redux": "^3.7.2",
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • HayatoKamono

    2018/03/23 14:55

    何をしたいのか、何に困っているのか、状況がよく伝わってこないので、もう少し詳細な内容を追記頂けますでしょうか?また、コンポーネントAとBを作って、それらを使っているコードを追記頂けますでしょうか?

    キャンセル

  • masaya_ohashi

    2018/03/23 15:03

    修正してみました。

    キャンセル

回答 3

checkベストアンサー

+1

初期の質問文から最終的な質問内容にだいぶ変化し、質問もシンプルなものになっていたため、元の回答を削除し、現在の質問文をベースにした回答に変更 - 2018/03/27

1

class Hoge extends Component {
  render() {
    return (
      <div>
        <div>
          <h1>Clicker 1</h1>
          <Clicker/>
        </div>
        <div>
          <h1>Clicker 2</h1>
          <Clicker/>
        </div>
      <div>;
  }
}

↑この場合であれば、Clickerコンポーネントをstoreに繋げるのではなく、Hogeコンポーネントをstoreに繋げて、Clickerが必要とするアクションやデータをpropsで渡してあげるのが一般的なやり方かと思います。

2

// reducers/clicker.js
const initialState = {value:0}


↓ 

// reducers/clicker.js
const initialState = [
  { id:1, value:0 },
  { id:2, value:0 }
];


Hogeコンポーネントでは、storeから参照できる上記のような配列をmap overしてclickerコンポーネントをレンダー。

3

// actions/clicker.js
function clickerClick() {
  return {type:'clicker.click'};
}


↓ 

function clickerClick(id) {
  return {
    type:'clicker.click',
    payload: id
  };
}


action creatorの引数でclickerのidを渡せるようにして、reducerがどのclicker idのデータかを判別できるようにし、クリックされたidのカウントをreducerでインクリメントして、新しいstateを返す。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/23 15:22

    もっと実際の使用シーンに沿ったクラス名に変えてみました。

    キャンセル

  • 2018/03/23 15:26

    回答のほう読ませていただきましたが、やはりstate内で各使用箇所でidを振ってやるしかないのでしょうか…その方法は思いついていたのですが、あまりにもスマートでないというか…こういう汎用的なものはReduxよりReactのstateに任せるべきですかね?

    キャンセル

  • 2018/03/23 15:32

    すみません。名前がHogeやClicker1,2になっても尚、具体的なシーンがわからず何にしたいのか分かってません。とりあえず、抽象的な回答を1つ追加すると、「 function clickerClick() { return {type:'clicker.click'}; } 」のclickerClickの引数に例えば、idなどの値を渡して、action objectにtypeだけではなく、引数に渡ってきた値も加えて、reducerに投げてあげれば、reducer側でなんか出来るのだと思います。(抽象的な回答にまたもやなりますが。。)

    キャンセル

  • 2018/03/23 15:33

    > こういう汎用的なものはReduxよりReactのstateに任せるべきですかね?

    Reduxを使うのであれば、Reactで持たせるstateはUIに関するstateにしてビューに徹させた方が良いかと思います。

    キャンセル

  • 2018/03/23 15:35

    私と同じ問題を抱えた人を見つけたので参考としてURLを貼りました。
    この人は「エディタ」を作ろうとして、同一ページ上に複数のエディタを並べたときに、それぞれ独立したデータを持たせたいようです。

    キャンセル

  • 2018/03/23 15:56 編集

    リンク先拝見しましたが、リンク先の方とmasaya_ohashiさんが同じ問題を抱えているのかどうかの判断は、自分にはこの質問文のコードからは付きませんでした。

    また、リンク先の2年ほど前の記事ですが、なぜ「全てのエディタコンポーネントで別のStoreを持つ」ことをしたいのかの理由が書かれていないようでしたし、その少し前に書かれていた「それを一つのStoreの中に配置しようとした時に、 エディタ側は渡されるStoreの構造に依存したくない」という部分に関しては同記事前半で述べられているpresentaional component とcontainer componentの分離でそれこそ解決することなのではと思いましたし、リンク先記事についてもいまいちよく分かりませんでした。

    とりあえず、私からは以上と致します。

    キャンセル

  • 2018/03/27 16:13

    これはわかりやすい回答ですね!
    なるほど親にひも付けをさせるという発想はなかったです。これなら様々な場所でidさえ割り当てればデータを別にできそうです。

    キャンセル

  • 2018/03/27 16:17

    おー、それは良かったです!最初のややこしい質問内容に引きずられていたみたいで、最終的に編集された内容に上手く回答出来てなかったみたいです。

    キャンセル

+1

質問者様がおっしゃっている通り、 同じreducerを利用している以上、同じstateを更新することしか出来ません。
reducerのstate内で分けて持つようなことも出来ますが、管理が煩雑になるためあまりおすすめ出来ません。

そもそも、reduxと紐づけたreact componentを複数箇所で使っている以上、同じデータを更新するのが期待される動作かと思いますので、別のデータを管理したいのであればcomponentを共通で使用すること自体設計が間違えているように思います。

Componentを共通で利用したい理由がデザインの共有等にあるのであれば、HoCを用いてデザインとClickイベントのみを持ったComponentと、Clickされたときの処理を定義するComponentに分けて実装するのがよいかと思います;。
HoCについては以下が参考になるかと。
https://postd.cc/react-higher-order-components-in-depth/
https://qiita.com/numanomanu/items/2b66d8b2887d44f857dc

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

https://redux.js.org/recipes/structuring-reducers/reusing-reducer-logic

公式的には

  • reducerで複数の名前で登録する
  • actionのtypeの末尾にIDを付与してdispatchする
  • 該当するreducerが受け取る
  • 各Componentは自身に紐付くstoreのデータを使う

というのが再利用可能reducerの作り方だそうです…1個2個程度ならいいですがたくさんの場所に使う場合、あまりにも不便過ぎます…

拡張パッケージ等でCoolなものがないか、しばらく質問は未解決のままにしておきます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.40%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • React.js

    961questions

    Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

  • Redux

    133questions