Redux: レデュヌサヌおよびコンポヌネント゚ンハンサヌずしおゞェネリックリストを䜜成するにはどうすればよいですか

䜜成日 2015幎09月30日  Â·  50コメント  Â·  ゜ヌス: reduxjs/redux

こんにちは。 カりンタヌの䟋を独立したカりンタヌの動的リストに拡匵するための良い方法は䜕でしょうか

動的ずは、カりンタヌのリストの最埌にあるUIに、新しいカりンタヌをリストに远加したり、最埌のカりンタヌを削陀したりするための+ $ボタンず-ボタンがあるこずを意味したす。

理想的には、カりンタヌレデュヌサヌずコンポヌネントはそのたたです。 あらゆる皮類の゚ンティティを収集するために、䞀般化されたリストストア+コンポヌネントをどのように䜜成したすか リストストア+コンポヌネントをさらに䞀般化しお、 todomvc-exampleからカりンタヌずtodo-itemsの䞡方を取埗するこずは可胜でしょうか

䟋にこのようなものがあるず䟿利です。

最も参考になるコメント

あらゆる皮類の゚ンティティを収集するために、䞀般化されたリストストア+コンポヌネントをどのように䜜成したすか リストストア+コンポヌネントをさらに䞀般化しお、todomvc-exampleからカりンタヌずtodo-itemsの䞡方を取埗するこずは可胜でしょうか

はい、間違いなく可胜です。
高次のレデュヌサヌず高次のコンポヌネントを䜜成する必芁がありたす。

高階レデュヌサヌに぀いおは、こちらのアプロヌチを参照しおください。

function list(reducer, actionTypes) {
  return function (state = [], action) {
    switch (action.type) {
    case actionTypes.add:
      return [...state, reducer(undefined, action)];
    case actionTypes.remove:
      return [...state.slice(0, action.index), ...state.slice(action.index + 1)];
    default:
      const { index, ...rest } = action;
      if (typeof index !== 'undefined') {
        return state.map(item => reducer(item, rest));
      }
      return state;
    }
  }
}

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return counter + 1;
  case 'DECREMENT':
    return counter - 1;
  }
}

const listOfCounters = list(counter, {
  add: 'ADD_COUNTER',
  remove: 'REMOVE_COUNTER'
});

const store = createStore(listOfCounters);
store.dispatch({
  type: 'ADD_COUNTER'
});
store.dispatch({
  type: 'ADD_COUNTER'
});
store.dispatch({
  type: 'INCREMENT',
  index: 0
});
store.dispatch({
  type: 'INCREMENT',
  index: 1
});
store.dispatch({
  type: 'REMOVE_COUNTER',
  index: 0
});

私はそれを実行しおいたせんが、最小限の倉曎で動䜜するはずです。

党おのコメント50件

これが良い䟋だず思いたす。
ReduxはここではElmArchitectureに䌌おいるので、そこにある䟋から自由にむンスピレヌションを埗おください。

あらゆる皮類の゚ンティティを収集するために、䞀般化されたリストストア+コンポヌネントをどのように䜜成したすか リストストア+コンポヌネントをさらに䞀般化しお、todomvc-exampleからカりンタヌずtodo-itemsの䞡方を取埗するこずは可胜でしょうか

はい、間違いなく可胜です。
高次のレデュヌサヌず高次のコンポヌネントを䜜成する必芁がありたす。

高階レデュヌサヌに぀いおは、こちらのアプロヌチを参照しおください。

function list(reducer, actionTypes) {
  return function (state = [], action) {
    switch (action.type) {
    case actionTypes.add:
      return [...state, reducer(undefined, action)];
    case actionTypes.remove:
      return [...state.slice(0, action.index), ...state.slice(action.index + 1)];
    default:
      const { index, ...rest } = action;
      if (typeof index !== 'undefined') {
        return state.map(item => reducer(item, rest));
      }
      return state;
    }
  }
}

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return counter + 1;
  case 'DECREMENT':
    return counter - 1;
  }
}

const listOfCounters = list(counter, {
  add: 'ADD_COUNTER',
  remove: 'REMOVE_COUNTER'
});

const store = createStore(listOfCounters);
store.dispatch({
  type: 'ADD_COUNTER'
});
store.dispatch({
  type: 'ADD_COUNTER'
});
store.dispatch({
  type: 'INCREMENT',
  index: 0
});
store.dispatch({
  type: 'INCREMENT',
  index: 1
});
store.dispatch({
  type: 'REMOVE_COUNTER',
  index: 0
});

私はそれを実行しおいたせんが、最小限の倉曎で動䜜するはずです。

ありがずう-私はこのアプロヌチを機胜させるように努めたす。

combineReducersを介しおリスト機胜を再利甚しお、カりンタヌのリストずToDoアむテムのリストの䞡方を取埗できるかどうかはただ疑問です。 そしおそれが理にかなっおいるなら。 しかし、私は間違いなくそれを詊しおみたす。

CombineReducersを介しおリスト機胜を再利甚しお、カりンタヌのリストずToDoアむテムのリストの䞡方を䜜成できるかどうかはただ疑問です。 そしおそれが理にかなっおいるなら。 しかし、私は間違いなくそれを詊しおみたす。

はい、完党に

const reducer = combineReducers({
  counterList: list(counter, {
    add: 'ADD_COUNTER',
    remove: 'REMOVE_COUNTER'
  }),
  todoList: list(counter, {
    add: 'ADD_TODO',
    remove: 'REMOVE_TODO'
  }),
});

@gaearonリストアクションはどのようにindexを取埗する必芁がありたすか

私たちはあなたの指瀺で高次のレデュヌサヌを䜜成するこずができたしたが、高次のコンポヌネントに苊劎しおいたす。 珟圚、私たちのコンポヌネントは、Counter以倖のコンポヌネントで䜿甚できるほど汎甚的ではありたせん。 私たちの問題は、䞀般的な方法でアクションにむンデックスを远加する方法です。

ここで私たちの゜リュヌションを芋るこずができたす https //github.com/Zeikko/redux/commit/6a222885c8c93950dbdd0d4cf3532cd99a32206c

問題のある郚分を匷調するために、コミットにコメントを远加したした。

カりンタヌのリストのリストを実行できる䞀般的なリストリデュヌサヌ+コンポヌネントがあるず䟿利です。

珟圚、私たちのコンポヌネントは、Counter以倖のコンポヌネントで䜿甚できるほど汎甚的ではありたせん。 私たちの問題は、䞀般的な方法でアクションにむンデックスを远加する方法です。

「䞀般的な方法でむンデックスを远加する」ずはどういう意味ですか アクションのindexキヌに異なる名前を付けたいずいうこずですか

私はあなたが今意味するこずを理解しおいるず思いたす。

申し蚳ありたせんが、今はあたりコメントできたせん。 明日たた戻りたす。

私は今問題を理解しおいたす。 それを調べたす。

ReduxがElmアヌキテクチャからどのように逞脱するかに固有のいく぀かの制限にすぐにぶ぀かりたした。
今たで理解できなかったのが残念です

  • コンポヌネントがラップされるたでに、コヌルバックではなくdispatchプロップを受け入れる必芁がありたす。 ぀たり、アクションクリ゚ヌタヌを小道具にするのを避けお、構成されたコンポヌネントでdispatch()を䜿甚するか、 connect() bindActionCreators(Component, actionCreators) => Componentを導入する必芁がありたす。ただし、実際にはストアに接続したせん。代わりに、action-creator-as-props-wanting-componentをthis.props.dispatch -wanting-componentに眮き換えたす。
  • ラップされたコンポヌネントからミドルりェアを必芁ずするアクションをディスパッチするこずはできたせん。 これは残念です カりンタヌをリストでラップするず、ディスパッチしたばかりのfunction (dispatch, getState) { ... }がリストによっお{ action: function (dispatch, getState) { ... } }に倉換されるため、$ incrementAsync()をディスパッチできなくなりたす。 サンクミドルりェアはそれを認識しなくなりたす。

これには厄介ではない解決策があるかもしれたせんが、私はただそれらを芋おいたせん。
今のずころ、このコミットを䟋ずしお芋おください䞊蚘の制限がありたす。

コヌドは次のずおりです。

components / Counter.js

import React, { Component, PropTypes } from 'react';
import { increment, incrementIfOdd, incrementAsync, decrement } from '../actions/counter';

class Counter extends Component {
  render() {
    const { dispatch, counter } = this.props;
    return (
      <p>
        Clicked: {counter} times
        {' '}
        <button onClick={() => dispatch(increment())}>+</button>
        {' '}
        <button onClick={() => dispatch(decrement())}>-</button>
        {' '}
        <button onClick={() => dispatch(incrementIfOdd())}>Increment if odd</button>
        {' '}
        <button onClick={() => dispatch(incrementAsync())}>Increment async</button>
      </p>
    );
  }
}

Counter.propTypes = {
  dispatch: PropTypes.func.isRequired,
  counter: PropTypes.number.isRequired
};

export default Counter;

components / list.js

import React, { Component, PropTypes } from 'react';
import { addToList, removeFromList, performInList } from '../actions/list';

export default function list(mapItemStateToProps) {
  return function (Item) {
    return class List extends Component {
      static propTypes = {
        dispatch: PropTypes.func.isRequired,
        items: PropTypes.array.isRequired
      };

      render() {
        const { dispatch, items } = this.props;
        return (
          <div>
            <button onClick={() =>
              dispatch(addToList())
            }>Add counter</button>

            <br />
            {items.length > 0 &&
              <button onClick={() =>
                dispatch(removeFromList(items.length - 1))
              }>Remove counter</button>
            }
            <br />
            {this.props.items.map((item, index) =>
              <Item {...mapItemStateToProps(item)}
                    key={index}
                    dispatch={action =>
                      dispatch(performInList(index, action))
                    } />
            )}
          </div>
        )
      }
    }
  };
}

アクション/counter.js

export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';

export function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

export function decrement() {
  return {
    type: DECREMENT_COUNTER
  };
}

export function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();

    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

export function incrementAsync(delay = 1000) {
  return dispatch => {
    setTimeout(() => {
      dispatch(increment());
    }, delay);
  };
}

アクション/list.js

export const ADD_TO_LIST = 'ADD_TO_LIST';
export const REMOVE_FROM_LIST = 'REMOVE_FROM_LIST';
export const PERFORM_IN_LIST = 'PERFORM_IN_LIST';

export function addToList() {
  return {
    type: ADD_TO_LIST
  };
}

export function removeFromList(index) {
  return {
    type: REMOVE_FROM_LIST,
    index
  };
}

export function performInList(index, action) {
  return {
    type: PERFORM_IN_LIST,
    index,
    action
  };
}

レデュヌサヌ/counter.js

import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter';

export default function counter(state = 0, action) {
  switch (action.type) {
  case INCREMENT_COUNTER:
    return state + 1;
  case DECREMENT_COUNTER:
    return state - 1;
  default:
    return state;
  }
}

レデュヌサヌ/list.js

import { ADD_TO_LIST, REMOVE_FROM_LIST, PERFORM_IN_LIST } from '../actions/list';

export default function list(reducer) {
  return function (state = [], action) {
    const {
      index,
      action: innerAction
    } = action;

    switch (action.type) {
    case ADD_TO_LIST:
      return [
        ...state,
        reducer(undefined, action)
      ];
    case REMOVE_FROM_LIST:
      return [
        ...state.slice(0, index),
        ...state.slice(index + 1)
      ];
    case PERFORM_IN_LIST:
      return [
        ...state.slice(0, index),
        reducer(state[index], innerAction),
        ...state.slice(index + 1)
      ];
    default:
      return state;
    }
  }
}

レデュヌサヌ/index.js

import { combineReducers } from 'redux';
import counter from './counter';
import list from './list'

const counterList = list(counter);

const rootReducer = combineReducers({
  counterList
});

export default rootReducer;

コンテナ/App.js

import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import Counter from '../components/Counter';
import list from '../components/list';

const CounterList = list(function mapItemStateToProps(itemState) {
  return {
    counter: itemState
  };
})(Counter);

export default connect(function mapStateToProps(state) {
  return {
    items: state.counterList
  };
})(CounterList);

cc @acdlite —これは珟圚のミドルりェア+ ReactReduxの蚭蚈が倚少壊れおいる䟋です。
これをwontfixずしお宣蚀するこずはできたすが、これを回避する方法があるかどうかを確認するこずをお勧めしたす。

だろう
https://github.com/erikras/multireducer/ヘルプ

私はReact甚のサヌビスIoCコンテナヌの䜿甚を実隓しおおり、昚日このテストリポゞトリを䜜成したした https //github.com/magnusjt/react-ioc

CounterListが知らないうちに、アクションクリ゚ヌタヌをカりンタヌに枡すこずができるので、問題の䞀郚を解決できる可胜性があるず思いたす。 これが可胜なのは、アクションの䜜成者が小道具ではなく、Counterのコンストラクタヌに入るからです。

䜜成する新しいCounterコンポヌネントごずに、異なるアクションクリ゚ヌタヌを枡すこずができたすおそらく、むンデックス倀をアクションクリ゚ヌタヌにバむンドするこずによっお。 もちろん、デヌタをカりンタヌに枡すこずにはただ問題がありたす。 それがサヌビスコンテナで解決できるものかどうかはただわかりたせん。

@gaearon 、あなたの䟋は私には正しいように芋えたす。 アクションクリ゚ヌタヌをパスしお、最埌たでディスパッチする必芁がありたす。 このようにしお、高階関数を䜿甚しおアクションを祭壇に眮くこずができたす。

ただし、2番目のポむントが必芁かどうかはわかりたせん。 新しいメッセヌゞ圢匏のためにミドルりェアを芋逃すこずになりたすが、 performInListの倧きな問題は、抜象化を1぀のリストだけに制限しおいるこずです。 @ pe3は、カりンタヌのリストのリストに぀いお蚀及したした。 そのような恣意的な抜象化のためには、䜕らかの圢でアクションをネストする必芁があるず思いたす。

このチケットでは、タむプをネストする方法を考え出したす。
https://github.com/rackt/redux/issues/897

しかし、もっず根本的には、アクションを完党にネストしたいず思うでしょう。

わかりたした、私はちょうどそれを詊しおみたした。

私は物事をかなり単玔化したした。 これは、この掟手なこずをする前のカりンタヌアプリです

https://github.com/ccorcos/redux-lifted-reducers/blob/80295a09c4d04654e6b36ecc8bc1bfac4ae821c7/index.js#L49

そしおここにそれは埌です

https://github.com/ccorcos/redux-lifted-reducers/blob/1fdafe8ed29303822018cde4973fda3305b43bb6/index.js#L57

「リフト」が適切な甚語かどうかはわかりたせん。関数型プログラミングで䜕か意味があるこずは知っおいたすが、私には問題ないず感じたした。

基本的に、アクションを解陀するこずで、アクションを別のアクション内にネストするこずになりたす。

const liftActionCreator = liftingAction => actionCreator => action => Object.assign({}, liftingAction, { nextAction: actionCreator(action) })

そしお、ネストされたアクションは、レデュヌサヌを持ち䞊げるこずによっお剥がされたす。 リフティングレデュヌサヌは基本的に、サブリデュヌサヌ適切なアクションで郚分的に適甚されたすをいく぀かのサブステヌトに適甚したす。

const liftReducer = liftingReducer => reducer => (state, action) => liftingReducer(state, action)((subState) => reducer(subState, action.nextAction))

したがっお、コンポヌネントレデュヌサヌのリストには、サブアクションが適甚されるむンデックスのコンポヌネントを指定するアクションがありたす。

// list actions
const LIST_INDEX = 'LIST_INDEX'
function actOnIndex(i) {
  return {
    type: LIST_INDEX,
    index: i
  }
}

そしお、私は「高次」ちょうど正しいず感じた別の掟手な甚語、ハハ;レデュヌサヌを持っおいたす。これはサブレデュヌサヌを適切なサブステヌトに適甚したす。

const list = (state=[], action) => (reduce) => {
  switch (action.type) {
    case LIST_INDEX:
      let nextState = state.slice(0)
      nextState[action.index] = reduce(nextState[action.index])
      return nextState
    default:
      return state;
  }
}

そしお、残っおいるのは、カりントレデュヌサヌをリストレデュヌサヌに「持ち䞊げる」こずだけです。

const reducer = combineReducers({
  counts: liftReducer(list)(count)
});

そしお、カりンタヌのリストに぀いおは、アクションをカりンタヌに枡すずきにアクションを解陀する必芁がありたす。

class App extends Component {
  render() {
    const counters = [0,1,2,3,4].map((i) => {
      return (
        <Counter count={this.props.state.counts[i]}
                 increment={liftActionCreator(actOnIndex(i))(increment)}
                 decrement={liftActionCreator(actOnIndex(i))(decrement)}
                 dispatch={this.props.dispatch}
                 key={i}/>
      )
    })
    return (
      <div>
        {counters}
      </div>
    );
  }
}

これは適切な甚語でより圢匏化できるず思いたす。 ここでは高次レデュヌサヌにもレンズが䜿えるず思いたすが、うたく䜿ったこずがありたせん。

そしお、最埌のコメントで蚀ったこずを取り戻したす- @ gaearonは正しいです。 このようにアクションをネストするず、ミドルりェアを芋逃すこずになり、アクションクリ゚ヌタヌを操䜜できるように、ディスパッチを最埌たで枡す必芁がありたす。 おそらくこれをサポヌトするために、Reduxはミドルりェアを介しおすべおのサブアクションを適甚する必芁がありたす。 たた、別の問題は、リスト内の状態を初期化するこずです...

あなたが説明しおいるのはElmArchitectureずしお知られおいたす。 こちらをご芧ください https //github.com/gaearon/react-elmish-example

おい、あなたはい぀も䞀歩先を行っおいたす クヌルなものぞのリンクで私にシャワヌを济びおください+1

ラップされたコンポヌネントからミドルりェアを必芁ずするアクションをディスパッチするこずはできたせん。 これは残念です カりンタヌをリストでラップするず、関数dispatch、getState{...}が{actionfunctiondispatch、getState{...}}に倉換されるため、incrementAsyncをディスパッチできなくなりたす。リスト—そしおバム サンクミドルりェアはそれを認識しなくなりたす。

@gaearonこの゜リュヌションはどうですか 䞀般的なレデュヌサヌが自分自身をこのような子レデュヌサヌず呌ぶ代わりに

case PERFORM_IN_LIST:
      return [
        ...state.slice(0, index),
        reducer(state[index], innerAction),
        ...state.slice(index + 1)
      ];

dispatchのように機胜する特別なメ゜ッドdispatchTo(reducer, state, action, callback)をストアに提䟛したす。ただし、子レデュヌサヌに盎接ディスパッチし構成されおいるすべおのミドルりェアを介しお、子の状態が倉曎されるたびにコヌルバックに通知したす。

export default function list(reducer, dispatchTo) {
  return function (state = [], action) {
    ...
    case PERFORM_IN_LIST:
      dispatchTo(reducer, state[index], innerAction, newState =>
         [
           ...state.slice(0, index),
           newState,
           ...state.slice(index + 1)
        ]);
       default:
          return state;
    }
  }
}

これがReduxで実行可胜かどうかはわかりたせん。 アむデアは、いく぀かのミドルりェアをルヌトストアずしお構成された状態ツリヌのその郚分の子ストアを返す内郚store.derive(reducer, state)メ゜ッドを䜿甚しおdispatchToを実装するこずです。 䟋えば

function dispatchTo(reducer, state, action, callback) {
  const childStore = store.derive(reducer, state)
  childStore.subscribe(() => setRootState( callback(getState() ))
  childStore.dispatch(action)
}

Reduxの内郚を知らないず蚀ったので、これは単なるアむデアなので、䜕かを逃したかもしれたせん

線集
_おそらく、レデュヌサヌメ゜ッドは同期的であるず想定されおいるため、これは厄介なこずになりたす。 メ゜ッドを非同期にするずいうこずは、すべおのチェヌンアップも非同期でなければならないこずを意味したす。
おそらく最善の解決策は、 store.derive(reducer)メ゜ッドを盎接公開し、ある皮のストア構成を䜿甚しお汎甚レデュヌサヌを構築するこずです_

それはあたりにも耇雑で、IMOの䟡倀はありたせん。 この方法で実行したい堎合は、ミドルりェアを䜿甚しないでくださいたたは、 applyMiddlewareの代替実装を䜿甚しおください。これで準備は完了です。

たた、これに察応する予定がないため、締め切りたす。

議論のために@gaearon 
このコミットで添付したコヌド䟋 https //github.com/rackt/redux/commit/a83002aed8e36f901ebb5f139dd14ce9c2e4cab4

カりンタヌのリストが2぀たたは別々の「モデル」ある堎合、アクションタむプが同じであるため、 addToListをディスパッチするず、䞡方のリストにアむテムが远加されたす。

// reducers/index.js

import { combineReducers } from 'redux';
import counter from './counter';
import list from './list'

const counterList = list(counter);
const counterList2 = list(counter);

const rootReducer = combineReducers({
  counterList,
  counterList2
});

export default rootReducer;

では、 reducers/listはここでどのように圹立ちたすか アクションタむプなどのプレフィックスを付ける必芁はありたせんか

カりンタヌのリストが2぀たたは別々の「モデル」ある堎合、アクションタむプが同じであるため、addToListをディスパッチするず䞡方のリストにアむテムが远加されたす。

a83002aed8e36f901ebb5f139dd14ce9c2e4cab4をよく芋おください。 アクションをネストしたす。 コンテナコンポヌネントから枡されるdispatchは、アクションをperformInListアクションにラップしたす。 次に、内郚アクションがレデュヌサヌで取埗されたす。 これは、Elmアヌキテクチャの動䜜ずほが同じです。

@gaearon䜕かが足りないかもしれたせんが、䞊蚘の远加のcounterList2レデュヌサヌずずもに、このUIは各アクションの䞡方のリストを曎新したすこれは、ビルド方法に応じお予想されたすが、解決策は䜕ですか

// reducers/index.js

import { combineReducers } from 'redux';
import counter from './counter';
import list from './list'

const counterList = list(counter);
const counterList2 = list(counter);

const rootReducer = combineReducers({
  counterList,
  counterList2
});

export default rootReducer;


// containers/App.js

import React from 'react';

import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import counter from '../components/Counter';
import list from '../components/list';

let CounterList = list(function mapItemStateToProps(itemState) {
  return {
    counter: itemState
  };
})(counter);

CounterList = connect(function mapStateToProps(state) {
  return {
    items: state.counterList
  };
})(CounterList);

let CounterList2 = list(function mapItemStateToProps(itemState) {
  return {
    counter: itemState
  };
})(counter);

CounterList2 = connect(function mapStateToProps(state) {
  return {
    items: state.counterList2
  };
})(CounterList2);


export default class App extends React.Component {
  render() {
    return (
      <div>
        <CounterList />
        <CounterList2 />
      </div>
    )
  }
}

@eladoカりンタヌのリストで行ったのず同じように、2぀のリストでアクションが衝突しないように、リストで再床ラップする必芁がありたす。

@ccorcos

もう䞀床リストにラップしたす

正確に䜕をラップしたすか

@ccorcos
ここに䟋をアップロヌドしたしたhttp //elado.s3-website-us-west-1.amazonaws.com/redux-counter/
゜ヌスマップがありたす

あなたが䜕を意味したのかただ完党にはわかりたせん。 私が蚀ったように、アクション名は同じであり、どのリストで実行するかがアクションクリ゚ヌタヌに瀺されおいないため、珟圚の動䜜は予想どおりです。したがっお、すべおのレデュヌサヌでアクションが実行され、最終的に䞡方のリストに圱響したす。

そのため、実際のReduxの機胜はよくわかりたせんが、elmに぀いおはよく知っおいたす。

この新しい䟋では、トップレベルのアクションクリ゚ヌタヌを拘束しおいたせん。 代わりに、ディスパッチ関数を䞋䜍コンポヌネントに枡し、それらの䞋䜍コンポヌネントはアクションをディスパッチ関数に枡すこずができたす。

抜象化をうたく機胜させおアクションの衝突を起こさないようにするために、「listOf」コンポヌネントがディスパッチをその子に枡すずき、実際には、リストコンポヌネントが理解できる圢匏でアクションをラップする関数を枡したす。

children.map((child, i) => {
  childDispatch = (action) => dispatch({action, index: i})
  // ...

これで、 listOf(counter)たたはlistOf(listOf(counter))を䜜成できたす。たた、 pairOfずいうコンポヌネントを䜜成する堎合は、アクションを枡すずきに必ずラップする必芁がありたす。 珟圚、 Appコンポヌネントは、ディスパッチ関数をラップせずにそれらを䞊べおレンダリングするだけなので、アクションの衝突が発生したす。

@ccorcos

ありがずう。 したがっお、結論ずしお、明らかに「魔法」は発生しおいたせん。アクションには、アクションを実行するむンスタンスをレデュヌサヌに指瀺するために必芁なすべおの情報が必芁です。 listOf(listOf(counter))の堎合、アクションには2次元配列の䞡方のむンデックスが必芁になりたす。 listOf(listOf(counter))は機胜したすが、アクションで枡される唯䞀のものである䞀意のIDでむンデックス付けされた単䞀のフラットリスト内のすべおのカりンタヌを䜿甚する方が柔軟性が高いようです。

柔軟で耇雑なReduxアプリを構築する唯䞀の方法は、システム内のすべおの゚ンティティをストア内のIDでむンデックス化するこずです。 䟋で瀺されるこずもある他のより単玔なアプロヌチは、すぐに限界に達したす。 このむンデックスは、ほずんどリレヌショナルDBのミラヌです。

アクションには、2次元配列の䞡方のむンデックスが必芁です

あなたの思考ず行動は次のように聞こえたす

{type: 'increment', index:[0 5]}

しかし、実際には次のようになりたす。

{type:'child', index: 0, action: {type: 'child', index: 5, action: {type: 'increment'}}}

そうすれば、 listOf(listOf(listOf(...listOf(counter)...)))氞遠に行うこずができたす

これはすべお゚ルムから来おいたす、ずころで。 Elmアヌキテクチャチュヌトリアルをご芧ください

パヌティヌには少し時間がかかりたすが、これが「ミドルりェアで機胜しない」堎所がわかりたせん。 @ccorcosずしお無限のネストを提䟛しおいる堎合、ネストを凊理するためにミドルりェアを接続するだけではいけたせんか それずも、ネストされたアクションが奇劙さを意味するredux-thunkに぀いおのみ話しおいるのでしょうか

ミドルりェアは、アクションをそのたた解釈するか、ネストされたアクションを探すかをどのように刀断したすか

そうですか。

@gaearon

こんにちは。

私はこの問題の解決策を理解したずは思っおいたせん。簡単な答えをいただければ幞いです。

同じペヌゞにコンポヌネントの耇数のコピヌがある堎合、_ミドルりェアおよび基本的にほずんどのRedux゚コシステムを䜿甚しないでください_ たた、誰かがマルチレデュヌサヌの提案に応答したかどうかもわかりたせんでした。

どんな説明でも圹に立ちたす。

いいえ、これは解決策ではありたせん。 スレッドは、ペヌゞ䞊にコンポヌネントの耇数のIDを持぀こずに぀いおではありたせん。 これを実装するには、アクションでIDを枡すだけです。 スレッドは_それを行うためのゞェネリック関数を曞くこず_に぀いおでした。 残念ながら、これはミドルりェアの抂念ず衝突したす。

ありがずう。

こんにちは、みんな、
たぶん私はこの問題を解決したした、しかし私はreact $よりもdekuを䜿うこずを奜みたす^^

この実隓、特にtaskMiddlewareのアむデアに぀いお、誰かが私に掞察を䞎えおくれたら嬉しいです。 @gaearonチェックする時間はありたすか 舌

カりンタヌのリスト

ありがずう

これらの゜リュヌションはいずれも初期状態の凊理を目的ずしおいないこずを明確にしおおきたいだけです。
これはどういうわけか@gaearonで達成できたすか 䞊蚘のリストレデュヌサヌがカりンタヌの初期リストを持぀こずを蚱可したすか

なぜそれが問題になるのでしょうか undefined状態の子レデュヌサヌを呌び出しお、それらの倀を䜿甚できるず思いたす。

ストアが最初のディスパッチで初期化されるずき、内郚ロゞックの堎合そのリストの初期状態が必芁です。これは、事前定矩されたカりンタヌのリストリストのアむテムのタむプである必芁がありたす。

このようなもの

export default function list(reducer) {
  return function (state = [

    // e.g. 2 counters with default values
    reducer(undefined, {}),
    reducer(undefined, {}),

      ], action) {
    const {
      index,
      action: innerAction
    } = action;
   // ...
  }
}

必芁に応じお、これをlistの匕数にするこずもできたす

これは本圓に耇雑に思えるので、1ペヌゞに同じコンポヌネントを耇数持぀こずができたす。

私はただReduxに頭を悩たせおいたすが、掚奚されるReduxの䜿甚パタヌンでなくおも、耇数のストアを䜜成する方がはるかに簡単なようです。

@deevus これらの議論のいく぀かを怖がらせないでください。 Reduxコミュニティには、関数型プログラミングを非垞に重芖しおいる人がたくさんいたす。このスレッドや他の同様の抂念で説明されおいる抂念のいく぀かには䟡倀がありたすが、それらは「完璧な」ものを目指す詊みのようなものになる傟向がありたす。 、単に「良い」ではなく。

䞀般に、1぀のペヌゞにコンポヌネントの耇数のむンスタンスを完党に含めるこずができたす。 この議論が目指しおいるのは、ネストされたコンポヌネントの任意の構成です。これは興味深いものですが、ほずんどのアプリが行う必芁のあるこずでもありたせん。

それ以倖に特定の懞念がある堎合は、通垞、スタックオヌバヌフロヌが質問をするのに適した堎所です。 たた、DiscordのReactifluxコミュニティには、Reactず関連テクノロゞヌに぀いお話し合うためのチャットチャネルがたくさんあり、話したり助けたりするこずをいずわない人が垞にいたす。

@markeriksonありがずう。 Reactiflux onDiscordから助けを求めおみたす。

これのフォロヌアップ
プロゞェクトでレデュヌサヌを再利甚するスケヌラブルな方法を芋぀けたした。レデュヌサヌ内でレデュヌサヌを再利甚するこずもできたす。

名前空間パラダむム

倚くの「郚分的な」レデュヌサヌの1぀に䜜甚するアクションは、同じアクションtypeをリッスンするため、レデュヌサヌが凊理するすべおのレデュヌサヌずは逆に、どのレデュヌサヌがアクションを凊理するかを決定する「名前空間」プロパティを保持するずいう抂念です䟋https://github.com/reactjs/redux/issues/822#issuecomment-172958967

ただし、名前空間を保持しないアクションは、他のレデュヌサヌ内のすべおの郚分レデュヌサヌに䌝播されたす。

郚分レデュヌサヌAがあり、初期状態がA(undefined, {}) === Saで、レデュヌサヌBが初期状態がB(undefined, {}) === { a1: Sa, a2: Sa }で、キヌがa1であるずしたす。 a2は、 Aのむンスタンスです。

['a1']の名前空間を持぀アクション*名前空間は垞に郚分レデュヌサヌに察する状態のキヌに䌌た文字列の順序付けられた配列です Bにキャストするず、次の結果が生成されたす

const action = {
  type: UNIQUE_ID,
  namespace: ['a1']
};

B(undefined, action) == { a1: A(undefined, action*), a2: Sa }

そしお、名前空間のないアクションの反䟋

const action = {
  type: UNIQUE_ID
};


B(undefined, action) == { a1: A(undefined, action), a2: A(undefined, action) }

è­Šå‘Š

  • Aが指定されたアクションを凊理しないレデュヌサヌを䜿い果たす堎合、同じ状態を返す必芁がありたす。これは、レデュヌサヌAでは凊理できないアクションpの堎合を意味したす。 B(undefined, p) { a1: Sa, a2: Sa }になるはずです。これは、ちなみにBの初期状態ず同じです。
  • 郚分的なレデュヌサヌ䞊蚘でaction*ずしお瀺されおいるに枡されるアクションは、スコヌプを絞り蟌むために䜿甚される名前空間を削陀する必芁がありたす。 したがっお、 Bに枡されたアクションが{ type: UNIQUE_ID, namespace: ['a1'] }だった堎合、 Aに枡されたアクションは{ type: UNIQUE_ID, namespace: [] }なりたす。
  • この構造を実珟するには、ストア内のすべおのレデュヌサヌが名前空間アクションを凊理する必芁がありたす。このポむントが満たされた堎合、䜿甚する名前空間の数ずネストできる郚分レデュヌサヌの数に制限はありたせん。

これを実珟するために、レデュヌサヌで名前空間を凊理するためのいく぀かの擬䌌コヌドを考え出したした。 これが機胜するためには、レデュヌサヌがアクションを凊理できるかどうか、およびレデュヌサヌに存圚する郚分的なレデュヌサヌの量を事前に知っおおく必芁がありたす。

(state = initialState, { ...action, namespace = [] }) => {
    var partialAction = { ...action, namespace: namespace.slice(1) };
    var newState;
    if (reducerCanHandleAction(reducer, action) and namespaceExistsInState(namespace, state)) {
        // apply the action to the matching partial reducer
        newState = {
            ...state,
            [namespace]: partialReducers[namespace](state[namespace], partialAction)
        };
    } else if (reducerCantHandleAction(reducer, action) {
        // apply the action to all partial reducers
        newState = Object.assign(
            {},
            state,
            ...Object.keys(partialReducers).map(
                namespace => partialReducers[namespace](state[namespace], action)
            )
        );
    } else {
        // can't handle the action
        return state;
    }

    return reducer(newState, action);
}

レデュヌサヌがアクションを凊理できるかどうかを事前に刀断する方法はあなた次第です。アクションタむプがキヌで、ハンドラヌ関数が倀であるオブゞェクトマップを䜿甚したす。

私はゲヌムに少し遅れるかもしれたせんが、圹立぀かもしれないいく぀かの汎甚レデュヌサヌを曞きたした
https://gist.github.com/crisu83/42ecffccad9d04c74605fbc75c9dc9d1

mutilreducerは玠晎らしい実装だず思いたす

@jeffhtli multireducerは、未定矩の量のレデュヌサヌを蚱可しないため、適切な゜リュヌションではありたせん。代わりに、静的レデュヌサヌリストを䜜成するように先制的に芁求したす。
代わりに、コンポヌネントのむンスタンスごずにUUIDを䜿甚し、UUIDごずに䞀意の状態を䜿甚しお、この問題を解決する小さなプロゞェクトを䜜成したした。
https://github.com/eloytoro/react-redux-uuid

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡