React 组件与 Redux store 数据的实时更新技巧

前言

在 React 开发中,Redux 被广泛应用来进行状态管理,它通过 store 的方式来保存全局状态,方便组件之间的数据传递和管理。然而,在实际的应用场景中,组件与 Redux store 的数据交互关系往往会面临一些挑战。比如,如何实现实时更新组件数据,又如何在不同组件间传递不同的状态等等。本文将从实际场景出发,介绍一些 React 组件与 Redux store 数据交互的技巧,希望对读者有指导意义。

实例背景

我们以一个在线购物车为例,来说明组件如何与 store 进行交互。

我们的购物车分为三个部分:商品列表,购物车列表和总价显示。商品列表中有多项商品,每项商品有自己的名称、价格和购买数量等属性。购物车列表中则记录了用户选购的商品以及数量,总价则是所有选购商品的价格和。

我们需要实现以下功能:

  • 在商品列表中添加选购商品,购物车列表中实时更新;
  • 在购物车列表中修改商品数量,总价实时更新;
  • 点击商品列表中的删除按钮,购物车列表中实时删除该商品,并更新总价。

解决方案

实时更新购物车列表

React 中的组件不能直接操作 store,所以我们需要使用 Redux 来进行数据管理。

下面是一个简单的购物车状态:

const initialState = {
  items: [],
  totalPrice: 0
};

我们需要实现的第一个功能是在商品列表中添加选购商品,购物车列表中实时更新。

为了实现此功能,我们需要在商品列表的每个 item 项上加上操作按钮。并且,我们还需要通过将 store 中的数据传给组件来实现组件和 store 中数据的联动。在组件中,我们需要调用 dispatch 来修改 store 中的数据。最终,购物车列表中会显示当前用户选购的商品。

以下是代码示例:

// 商品列表
function ProductList(props) {
  const dispatch = useDispatch();
  const products = useSelector(state => state.products);

  // 点击添加按钮 callback
  const handleOnClick = (product) => {
    const action = { type: 'ADD_ITEM', payload: product };
    dispatch(action);
  };

  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>
          <span>{product.name}</span>
          <span>{product.price}</span>
          {/* 添加按钮点击事件 */}
          <button onClick={() => handleOnClick(product)}>Add</button>
        </li>
      ))}
    </ul>
  );
}

// 购物车列表
function CartList(props) {
  const dispatch = useDispatch();
  const cartItems = useSelector(state => state.cart.items);
  const totalPrice = useSelector(state => state.cart.totalPrice);

  // 修改商品数量 callback
  const handleOnQtyChange = (item, qty) => {
    const action = { type: 'UPDATE_QTY', payload: { item, qty } };
    dispatch(action);
  };

  // 删除商品 callback
  const handleOnDelete = (item) => {
    const action = { type: 'DELETE_ITEM', payload: item };
    dispatch(action);
  };

  return (
    <div>
      <ul>
        {cartItems.map(item => (
          <li key={item.id}>
            <span>{item.name}</span>
            <span>{item.price}</span>
            <span>{item.qty}</span>
            {/* 数量修改事件 */}
            <input value={item.qty} onChange={(e) => handleOnQtyChange(item, e.target.value)} />
            {/* 删除按钮事件 */}
            <button onClick={() => handleOnDelete(item)}>Delete</button>
          </li>
        ))}
      </ul>
      <div>
        Total Price: {totalPrice}
      </div>
    </div>
  );
}

在上述代码中,我们可以看到:

  • 在商品列表中,我们通过调用 dispatch 来触发 ADD_ITEM 的 action,从而在 store 中添加商品;
  • 在购物车列表中,我们使用 useSelector 来获取 store 中的数据,并通过 handleOnQtyChange 和 handleOnDelete callback 来实现数量修改和删除商品的操作。

由于 store 的数据是实时更新的,所以购物车列表中的商品和总价会随着用户操作实时更新。

传递不同的状态

在购物车应用场景中,一个商品的状态包括其选购数量和购物车中已购买的状态。同时,商品列表和购物车列表是由不同的组件渲染的,所以我们需要在不同的组件中传递商品状态。

我们可以通过 store 中的数据来实现组件数据的同步。当用户在商品列表中点击 Add 按钮时,我们将商品数据传递给 store,store 将其作为新的购物车条目添加到 cart.items 中。当用户在购物车列表中修改某一个商品的数量时,我们将其更新后的数量也作为新的购物车条目添加到 cart.items 中。

当然,我们还需要实现当用户在商品列表中添加重复的商品时,只更新该商品的数量,而不是添加一个新的购物车条目。

以下是代码示例:

// 使用 combineReducer 来合并不同的 reducer
const rootReducer = combineReducers({
  products: productsReducer,
  cart: cartReducer,
});

// 产品 reducer
function productsReducer(state = productsData, action) {
  switch (action.type) {
    default:
      return state;
  }
}

// 购物车 reducer
function cartReducer(state = initialState, action) {
  let newState;

  switch (action.type) {
    case 'ADD_ITEM':
      // 查找购物车中是否存在该商品
      const existingItemIndex = state.items.findIndex(item => item.id === action.payload.id);
      if (existingItemIndex !== -1) {
        // 商品存在,修改其数量
        newState = {
          ...state,
          items: [...state.items],
        };
        newState.items[existingItemIndex].qty += 1;
      } else {
        // 商品不存在,添加新条目
        newState = {
          ...state,
          items: [
            ...state.items,
            {
              ...action.payload,
              qty: 1,
            }
          ],
        };
      }

      // 计算总价
      newState.totalPrice = newState.items.reduce((total, item) => (
        total + item.qty * item.price
      ), 0);

      return newState;

    case 'UPDATE_QTY':
      // 查找商品
      const itemIndex = state.items.findIndex(item => item.id === action.payload.item.id);
      if (itemIndex !== -1) {
        // 商品存在,更新其数量
        newState = {
          ...state,
          items: [...state.items],
        };
        newState.items[itemIndex].qty = action.payload.qty;
      }

      // 计算总价
      newState.totalPrice = newState.items.reduce((total, item) => (
        total + item.qty * item.price
      ), 0);

      return newState;

    case 'DELETE_ITEM':
      // 查找商品
      const itemIndex = state.items.findIndex(item => item.id === action.payload.id);
      if (itemIndex !== -1) {
        // 商品存在,删除其
        newState = {
          ...state,
          items: [...state.items],
        };
        newState.items.splice(itemIndex, 1);
      }

      // 计算总价
      newState.totalPrice = newState.items.reduce((total, item) => (
        total + item.qty * item.price
      ), 0);

      return newState;

    default:
      return state;
  }
}

在上述代码中,我们可以看到:

  • 在 ADD_ITEM 的操作中,我们通过查找购物车中是否存在该商品来实现购物车条目的更新或添加。并且,通过计算所有条目的价格和来得到价格的总和;
  • 在 UPDATE_QTY 的操作中,我们同样通过查找商品,来更新其数量,并得到价格的总和;
  • 在 DELETE_ITEM 的操作中,我们同样通过查找商品,来实现购物车条目的删除,并得到价格的总和。

总结

在本文中,我们介绍了如何实现实时更新 React 组件与 Redux store 的数据交互,以及如何在不同的组件中传递不同的状态。通过实际的应用场景和示例代码,我们详细介绍了如何使用 React 和 Redux 库来完成在线购物车的开发。这些技巧也可以应用到其他不同的应用场景中,帮助开发者更好地管理数据,并实现更具交互性的应用程序。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a0a24eadd4f0e0ff8e39d3


纠错反馈