Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux / Redux Saga - how to wait for the store to update?

I am using React with Redux and Sagas to create a card game simulator. I need to create a function which updates the redux state, waits for the new state and runs again. I'm not sure if / how something like that is possible in React Redux land. Below is a simplified version of what I'm trying to achieve:

function endTurn() {
  // do something
}

function playerTurn(playerHand) {
  const decision = getPlayerDecision(playerHand);
  updatePlayerHand(decision); // this dispatches an action to update redux state

  // If player chose to 'Stand', the game ends.
  // Otherwise, it's player's turn again.
  if(decision === 'Stand') {
    endTurn();
  } else {
    // here I need the updated hand, how do I get it?
    playerTurn(updatedHand);
  }
}

An obvious solution would be to place this logic in 'componentWillReceiveProps' but it doesn't seem right and I'm sure it would end up being very buggy. Intuitively, it feels like a job for Redux Saga but I haven't been able to find anything relevant in the docs. Any suggestions?

SOLUTION: Krasimir's answer with the use of yield select pointed me in the right direction. Below is a simplified version of the code I ended up with:

import { put, takeEvery, select } from 'redux-saga/effects';

function* playPlayerTurn() {

  const playerHand = yield select(getPlayerHand);
  const decision = getPlayerDecision(playerHand);

  // some action that executes the decision
  // the action results in a change to playerHand
  yield put({
    type: `PLAY_PLAYER_DECISION`,
    decision,
  });

  // If player chose to 'Stand', the turn ends.
  // Otherwise, it's player's turn again.
  if(decision === 'Stand') {
    console.log('PLAYER OVER');
  } else {
    console.log('PLAYER AGAIN');
    yield put({
      type: `PLAY_PLAYER_TURN`,
    });
  }
}

export function* playerTurnWatcher() {
  yield takeEvery(`PLAY_PLAYER_TURN`, playPlayerTurn);
}

Essentially, I'm able to call the saga recursively

like image 916
user2634633 Avatar asked Jan 09 '18 11:01

user2634633


People also ask

How do you wait in Redux-saga?

You need to do something - as written this will call sleep (returning a promise) and then immediately throw it away! If you yield a call() then redux-saga will wait for the promise to resolve, e.g.: yield call(sleep, 2) . The best solution is in the answer above - using the delay utility function.

How do we update the Redux store?

First we find the index of the item in the State array by using findIndex. In the picture above, x+1 is the index of the item needs updating. Once the updated object is created, the quickest way to update is using the slice method and spread operator (…).

What creates an Effect description that instructs the middleware to wait for a specified action?

take(pattern) ​ Creates an Effect description that instructs the middleware to wait for a specified action on the Store. The Generator is suspended until an action that matches pattern is dispatched. The result of yield take(pattern) is an action object being dispatched.

What happens when Redux state changes?

The fact that the Redux state changes predictably opens up a lot of debugging possibilities. For example, using time travel makes it possible to travel back and forth between different states instead of having to reload the whole app in order to get back to the same place.


1 Answers

Let's assume that you dispatch an action PLAYER_HAND where the payload is the decision.

import { select, takeLatest } from 'redux-saga/effects';

const waitForPlayerTurn = function * () {
  takeLatest(PLAYER_HAND, function * (decision) {
    // at this point the store contains the right `decision`
    if(decision === 'Stand') {
      endTurn();
    } else {
      playerTurn(yield select(getUpdateHand)); // <-- getUpdateHand is a selector
    }
  });
}

And of course you have to run the waitForPlayerTurn saga.

like image 151
Krasimir Avatar answered Sep 24 '22 04:09

Krasimir