【问题标题】:Adding an Object to a Nested Array in Redux React在 Redux React 中将对象添加到嵌套数组
【发布时间】:2020-07-02 07:30:49
【问题描述】:

我正在尝试在paymentMethods 数组内的banking_cards array 中添加一张新卡。 banking_cards 数组位于 paymentMethods 数组内。所以我想在banking_cards 数组中插入新的卡片对象。我下面的代码产生了一个错误,上面写着state.paymentMethods.banking_cards is not iterable

注意

banking_cards 数组在paymentMethods 数组内

export const initialState = {
    paymentMethods: [],
  };



  case paymentMethodConstants.ADD_CARD_SUCCESS:
    return {
      ...state,
      paymentMethods: [
        ...state.paymentMethods,
        banking_cards: [...state.paymentMethods.banking_cards, action.payload],
      ],
    };

JSON

paymentMethods = [
  {
    "id": 8,
    "customer_token": "epofjoe",
    "banking_cards": [
      {
        "id": 1,
        "banking_token": "AAA",
        "last_4": "0006",
        "exp_year": 2021,
        "exp_month": 12,
        "cvc": 876
      },
      {
        "id": 2,
        "banking_token": "BBB",
        "last_4": "0002",
        "exp_year": 2022,
        "exp_month": 12,
        "cvc": 877
      },
    ]
  }
]

【问题讨论】:

  • initialState 中,paymentMethods 是一个数组,但在你的reducer 函数中,它是一个对象?因为你有paymentMethods: {...
  • @tanmay。 paymentMethods 应该是一个数组。 paymentMethods 包含 banking_cards,它也是一个数组。我需要在 banking_cards 中添加新的卡片对象
  • javascript 中的数组不能包含字符串键...所以banking_cards 没有任何意义。添加你想要实现的结构。
  • @StackedQ。请参阅已编辑的问题。如您所见,banking_cardspaymentMethods 数组中的一个数组。当我想添加一张新卡时,它应该插入到banking_cards 那里。
  • @YevgenGorbunkov。你能重写我的代码吗?谢谢

标签: javascript reactjs redux ecmascript-6 react-redux


【解决方案1】:

将卡添加到支付方式时,您的操作需要包含卡需要添加到的支付方式。

下面是一个工作示例,说明如何做到这一点:

const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;

const createId = ((id) => () => id++)(3);

const initialState = {
  paymentMethods: [
    {
      id: 8,
      banking_cards: [
        {
          id: 1,
        },
        {
          id: 2,
        },
      ],
    },
    {
      id: 9,
      banking_cards: [
        {
          id: 1,
        },
        {
          id: 2,
        },
      ],
    },
  ],
};
//action types
const ADD_CARD_SUCCESS = 'ADD_CARD_SUCCESS';
//action creators
const addCardSuccess = (payementMethodId, card) => ({
  type: ADD_CARD_SUCCESS,
  payload: { payementMethodId, card },
});
const reducer = (state, { type, payload }) => {
  if (type === ADD_CARD_SUCCESS) {
    const { payementMethodId, card } = payload;
    return {
      ...state,
      paymentMethods: state.paymentMethods.map((method) =>
        method.id === payementMethodId
          ? {
              ...method,
              banking_cards: [
                ...method.banking_cards,
                card,
              ],
            }
          : method
      ),
    };
  }
  return state;
};
//selectors
const selectPaymentMethods = (state) =>
  state.paymentMethods;
//creating store with redux dev tools
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  initialState,
  composeEnhancers(
    applyMiddleware(() => (next) => (action) =>
      next(action)
    )
  )
);
//pure component so not re rendering when nothing changed
const Card = React.memo(function Card({ card }) {
  return <li>card: {card.id}</li>;
});
//pure component so it won't re render when nothing changes
const PaymentMethod = React.memo(function PaymentMethod({
  paymentMethod,
}) {
  const dispatch = useDispatch();
  return (
    <li>
      payment method id: {paymentMethod.id}
      <ul>
        {paymentMethod.banking_cards.map((card) => (
          <Card key={card.id} card={card} />
        ))}
      </ul>
      <button
        onClick={() =>
          dispatch(
            //dispatch action with payment method id
            addCardSuccess(paymentMethod.id, {
              //the new card to be added
              id: createId(),
            })
          )
        }
      >
        Add card
      </button>
    </li>
  );
});
const App = () => {
  const paymentMethods = useSelector(selectPaymentMethods);
  return (
    <ul>
      {paymentMethods.map((paymentMethod) => (
        <PaymentMethod
          key={paymentMethod.id}
          paymentMethod={paymentMethod}
        />
      ))}
    </ul>
  );
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<div id="root"></div>

有了 immer,我会使用支付方式索引而不需要地图:

const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { produce } = immer;

const createId = ((id) => () => id++)(3);

const initialState = {
  paymentMethods: [
    {
      id: 8,
      banking_cards: [
        {
          id: 1,
        },
        {
          id: 2,
        },
      ],
    },
    {
      id: 9,
      banking_cards: [
        {
          id: 1,
        },
        {
          id: 2,
        },
      ],
    },
  ],
};
//action types
const ADD_CARD_SUCCESS = 'ADD_CARD_SUCCESS';
//action creators
const addCardSuccess = (index, card) => ({
  type: ADD_CARD_SUCCESS,
  payload: { index, card },
});
const reducer = (state, { type, payload }) => {
  if (type === ADD_CARD_SUCCESS) {
    //lot less hassle using index and immer
    const { index, card } = payload;
    return produce(state, (draft) => {
      draft.paymentMethods[index].banking_cards.push(card);
    });
  }
  return state;
};
//selectors
const selectPaymentMethods = (state) =>
  state.paymentMethods;
//creating store with redux dev tools
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  initialState,
  composeEnhancers(
    applyMiddleware(() => (next) => (action) =>
      next(action)
    )
  )
);
//pure component so not re rendering when nothing changed
const Card = React.memo(function Card({ card }) {
  return <li>card: {card.id}</li>;
});
//pure component so it won't re render when nothing changes
const PaymentMethod = React.memo(function PaymentMethod({
  paymentMethod,
  index,
}) {
  const dispatch = useDispatch();
  return (
    <li>
      payment method id: {paymentMethod.id}
      <ul>
        {paymentMethod.banking_cards.map((card) => (
          <Card key={card.id} card={card} />
        ))}
      </ul>
      <button
        onClick={() =>
          dispatch(
            //dispatch action with payment method index
            addCardSuccess(index, {
              //the new card to be added
              id: createId(),
            })
          )
        }
      >
        Add card
      </button>
    </li>
  );
});
const App = () => {
  const paymentMethods = useSelector(selectPaymentMethods);
  return (
    <ul>
      {paymentMethods.map((paymentMethod, index) => (
        <PaymentMethod
          key={paymentMethod.id}
          paymentMethod={paymentMethod}
          index={index}
        />
      ))}
    </ul>
  );
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://unpkg.com/immer@7.0.5/dist/immer.umd.production.min.js"></script>
<div id="root"></div>

【讨论】:

  • 请使用 immer 移除。谢谢
  • @Robert 请参阅下面的评论,您可以使用 ? {...method,banking_cards:[...method.banking_cards,card]} 代替 immer。如果 immer 是给定的,我会使用付款方式索引,并且 reducer 会更简单(不需要地图)。 Immer 将成为现在的东西,因为它包含在 redux template
  • 谢谢。你能分开使用 immer 和不使用 immer 的代码吗?将它们分成两个文件,而不仅仅是注释掉,以便我更好地理解。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-10
  • 1970-01-01
  • 2020-11-14
相关资源
最近更新 更多