【问题标题】:Emitting events from Redux reducers从 Redux reducer 发出事件
【发布时间】:2015-12-07 16:21:28
【问题描述】:

我在一个简单的射击游戏中使用redux 作为状态容器。状态是完全确定的,系统接收的唯一输入是用户输入(例如,武器被开火等)。 我的问题是我必须跟踪(和处理)在游戏期间发生的某些事件(例如,某些东西被破坏等),我不太确定如何做到这一点。 我目前的解决方案是reducer在当前状态下维护一个events数组,每个reducer只是追加事件。

FIRE_WEAPON+-+          FIRE_WEAPON+-+
             |                       |
             |                       |
           +-v--------+--------------v------------->
                      |
                      |
                      +->PLAYER_DESTROYED

这里reduce 接收两个FIRE_WEAPON 动作,并且应该“发射”一个PLAYER_DESTROYED 事件(现在,它用于在那里渲染爆炸)。

project 是开源的,reducer 看起来像这样(这只是伪代码,但这里是relevant game logic):

// combine is just (f, g) => ((s, a) => g(f(s, a), a))
const reducer = combine(

  // simulate world
  (state, action) => {
    while (state.time < action.time) {
      state = evolve(state, delta); // evolve appends the happened in-game events to the state
    }
    return state;
  },

  // handle actual user input
  (state, action) => {
    return handleUserInput(state, action);
  }
);

const evolve = (state, delta) => {
  const events = [];

  // some game logic that does some `events.push(...)`

  return {
    ...state,
    time: state.time + delta,
    events: state.events.concat(events),
  };
}

我们可以假设,handleUserInput 是一个简单的x =&gt; x 标识函数(它不涉及events 数组)。 在evolve 期间,我想“发出” 事件,但由于这会使evolve 不纯,我不能这样做。 正如我所说,现在我通过将发生的事件存储在状态中来做到这一点,但可能有更好的方法。有什么建议吗?

这些events在渲染过程中使用,看起来像这样:

let sprites = [];

// `subscribe`d to store
function onStateChange() {
  // `obsolete` removes old sprites that should not be displayed anymore
  // `toSprite` converts events to sprites, you can assume that they are just simple objects
  sprites = sprites.filter(obsolete).concat(store.getState().events.map(toSprite));
}

function render(state) {
  renderState(state);
  renderSprites(sprites);
}

但稍后我想在服务器上使用events(上面描述的减速器也在服务器上运行),用于计算各种统计数据(例如,被摧毁的敌人等)。

Ps.:这些 "emitted" 事件对状态没有影响(它们完全不相关),所以我确信它们不应该是动作(因为它们会离开状态不变)。它们在 reducer 完成后被处理,之后它们可以被删除(reducer 总是收到一个空的events 数组)。

【问题讨论】:

  • 您能否添加一些关于如何使用events 以及它们何时不再有用的信息?你不想永远积累它们。
  • @DanAbramov 帖子​​已更新!
  • 恐怕我还没有足够的上下文。你能展示一下combine是什么,handleUserInput的样子,最重要的是,实际使用state.events的代码在哪里。
  • @DanAbramov 我希望现在更清楚
  • 如果events 总是可以从当前的state 计算出来并且不需要存储,似乎你应该在reducers 外部计算它们。例如,请参阅github.com/rackt/redux/blob/master/docs/recipes/…。这有帮助吗?

标签: javascript redux


【解决方案1】:

我很确定你可以把它分成三个部分:

-动作:

  const fireWeapon = ()=>({
   type: FIRE_WEAPON
   })

您可以启动诸如 fireWeapon okey 之类的操作,正如您所说,reducer 是纯函数,因此您可以在状态中存储您启动该操作的次数。

-Reducer 触发

 initialState: { fireWeapon: 0, fireShotgun:0}

 CASE FIRE_WEAPON: 
   return {...state, fireWeapon: state.fireWeapon+1}

最后,关键部分,一个名为 redux-observable 的库,它基于 rxjs,反应式编程。您可以订阅动作流并发出新动作。

一个非常简单的例子是:

export const clearDeletedSiteEpic = (action$,state$) =>
  action$.pipe(
    ofType(FIRE_WEAPON),
    map(() => {
     if (state$.value.fires.fireWeapon % 2 === 0){
       playerDestroyed() // action is launched 
     }
   }
  );

【讨论】:

    猜你喜欢
    • 2021-06-02
    • 1970-01-01
    • 2019-01-18
    • 2017-09-28
    • 2019-05-07
    • 2016-10-06
    • 2022-07-31
    • 1970-01-01
    • 2016-05-18
    相关资源
    最近更新 更多