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
相关产品推荐
相关产品推荐

