You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Redux+Redux-thunk卡牌游戏:分发含Store/API参数的Action及手牌实现

嘿,我来一步步帮你搞定这个抽牌功能!咱们从调整Store结构开始,再到action和reducer的实现,全程结合Redux-thunk来处理依赖Store状态和API的逻辑~

实现步骤拆解

1. 更新Store的初始状态结构

首先,你需要在reducer的初始state里加入hand属性,用来存储玩家手牌。假设你之前的state已经有deck(存储从API获取的卡组),那初始状态应该是这样的:

const initialState = {
  deck: [], // 之前从API获取的卡牌数组
  hand: [], // 新增:玩家手牌数组
  isLoading: false, // 可选:处理API加载状态
  error: null // 可选:处理API错误
};

2. 定义Action Types

先把需要用到的action类型统一定义,避免拼写错误:

// actionTypes.js
export const DRAW_CARD = 'DRAW_CARD';
export const REFILL_DECK_SUCCESS = 'REFILL_DECK_SUCCESS';
export const REFILL_DECK_FAILURE = 'REFILL_DECK_FAILURE';

3. 编写Action Creators(同步+异步Thunk)

同步Action Creator(抽牌成功)

这个action用来通知reducer更新手牌和卡组:

// actions.js
import { DRAW_CARD, REFILL_DECK_SUCCESS, REFILL_DECK_FAILURE } from './actionTypes';

// 同步action:抽牌成功
export const drawCard = (drawnCard) => ({
  type: DRAW_CARD,
  payload: drawnCard
});

异步Thunk Action Creator(核心逻辑:结合Store状态+API)

这里就是你需要的「同时依赖Store数据和API的action」:通过Redux-thunk提供的getState方法获取当前Store里的卡组状态,判断卡组是否为空;如果空了就调用API重新获取卡组,再执行抽牌逻辑。

// 异步thunk action:从卡组抽牌(卡组空了自动调用API补全)
export const drawCardFromDeck = () => async (dispatch, getState) => {
  const { deck } = getState().cardGame; // 假设你的reducer挂载在cardGame节点下

  // 情况1:卡组还有牌,直接抽
  if (deck.length > 0) {
    // 示例:抽卡组的第一张牌,你也可以改成随机抽牌逻辑
    const drawnCard = deck[0];
    dispatch(drawCard(drawnCard));
    return;
  }

  // 情况2:卡组空了,先调用API获取新卡组,再抽牌
  try {
    // 这里复用你之前获取卡组的API请求逻辑
    const response = await fetch('你的卡牌API地址');
    const newDeck = await response.json();

    // 先分发更新卡组的action
    dispatch({
      type: REFILL_DECK_SUCCESS,
      payload: newDeck
    });

    // 从新卡组抽第一张牌
    if (newDeck.length > 0) {
      dispatch(drawCard(newDeck[0]));
    }
  } catch (error) {
    // 处理API请求失败的情况
    dispatch({
      type: REFILL_DECK_FAILURE,
      payload: error.message
    });
  }
};

4. 更新Reducer处理新的Actions

修改你的reducer,处理新增的action类型,必须注意保持state不可变(不能直接修改原state):

// reducer.js
import { DRAW_CARD, REFILL_DECK_SUCCESS, REFILL_DECK_FAILURE } from './actionTypes';
import initialState from './initialState';

const cardGameReducer = (state = initialState, action) => {
  switch (action.type) {
    case DRAW_CARD:
      return {
        ...state,
        // 从卡组移除抽到的牌(slice(1)返回除第一张外的所有牌)
        deck: state.deck.slice(1),
        // 把手牌数组拷贝并添加新抽到的牌
        hand: [...state.hand, action.payload]
      };
    case REFILL_DECK_SUCCESS:
      return {
        ...state,
        deck: action.payload,
        error: null
      };
    case REFILL_DECK_FAILURE:
      return {
        ...state,
        error: action.payload
      };
    // 保留你之前已有的其他case(比如获取初始卡组的action)
    default:
      return state;
  }
};

export default cardGameReducer;

5. 在组件中分发Thunk Action

最后,在React组件里通过Redux的hooks或者connect方法分发这个thunk action:

Hooks写法示例:

import { useDispatch } from 'react-redux';
import { drawCardFromDeck } from './actions';

function DrawCardButton() {
  const dispatch = useDispatch();

  const handleDraw = () => {
    dispatch(drawCardFromDeck());
  };

  return <button onClick={handleDraw}>抽一张牌</button>;
}

类组件写法示例:

import { connect } from 'react-redux';
import { drawCardFromDeck } from './actions';

class DrawCardButton extends React.Component {
  handleDraw = () => {
    this.props.drawCardFromDeck();
  };

  render() {
    return <button onClick={this.handleDraw}>抽一张牌</button>;
  }
}

export default connect(null, { drawCardFromDeck })(DrawCardButton);

关键注意点

  • Immutable状态更新:永远不要直接修改state,要用扩展运算符...、slice、filter等方法返回新的state对象/数组,否则Redux无法检测到状态变化。
  • Thunk的getState用法:通过getState可以获取当前Store的所有状态,这样你就能在action里根据现有状态(比如卡组是否为空)来决定后续逻辑。
  • API错误处理:在thunk里加上try/catch,处理API请求失败的情况,避免程序崩溃。

内容的提问来源于stack exchange,提问作者Przemek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:35:47