ctrlplusb / easy-peasy

Vegetarian friendly state for React
https://easy-peasy.dev
MIT License
5.03k stars 190 forks source link

RangeError "Maximum call stack size exceeded" on clone function #797

Closed lunacer closed 1 year ago

lunacer commented 1 year ago

Hi, I'm facing "RangeError: Maximum call stack size exceeded" It's on the "clone" function in lib.js, and the error message points the location "at Function.getOwnPropertyDescriptor ()" I'm currently on version 5.1.0

I thought it was probably my model but can't find out the issue with it. Could you take a look?

The error message is;

index.js:172 Uncaught (in promise) RangeError: Maximum call stack size exceeded at Function.getOwnPropertyDescriptor () at index.js:172:1 at Array.reduce () at recursiveClone (index.js:171:1) at index.js:179:1 at Array.forEach () at recursiveClone (index.js:177:1) at index.js:179:1 at Array.forEach () at recursiveClone (index.js:177:1)

And my model is;

import { action } from "easy-peasy";

const model = {
  //states
  userSession: {
    isLoggedIn: false,
    user: {},
    CartDetail: [],
    DiscountArray: [],
    ShipCost: 3500,
    PayAmount: 0,
  },
  productList: {
    data: [],
    filters: {
      tags: [],
      type: [],
      category: [],
      tax: [],
      status: [],
      badge: [],
    },
  },
  data: {
    data: [],
    filters: {
      categories: [],
      topics: [],
      tags: [],
      discount: [],
      status: [],
      order_status: [],
      session: [],
      payment_method: [],
    },
  },
  language: "ko",

  //actions
  authenticate: action((state, authData) => {
    state.userSession.user = authData;
  }),
  loginState: action((state) => {
    state.userSession.isLoggedIn = state;
  }),
  setLanguage: action((state, lang) => {
    state.language = lang;
  }),
  setProdData: action((state, data) => {
    state.productList.data = data;
  }),
  setProdFilters: action((state, filters) => {
    state.productList.filters = filters;
  }),
  addUserFav: action((state, productId) => {
    const favorite = state.userSession.user.favorite;
    favorite.push(productId);
  }),
  removeUserFav: action((state, productId) => {
    const favorite = state.userSession.user.favorite;
    if (favorite && favorite.length > 0) {
      favorite.forEach((obj, i) => {
        if (obj === productId) {
          favorite.splice(i, 1);
        }
      });
    }
  }),
  setUserCart: action((state, cartData) => {
    state.userSession.user.cart = cartData;
  }),
  createGuestCart: action((state) => {
    state.userSession.GuestCart = [];
  }),
  removeGuestCart: action((state) => {
    delete state.userSession.GuestCart;
  }),
  pushGuestCart: action((state, cartData) => {
    state.userSession.GuestCart.push(cartData);
  }),
  setGuestCart: action((state, cartData) => {
    state.userSession.GuestCart = cartData;
  }),
  removeFromGuestCart: action((state, cartData) => {
    state.userSession.GuestCart = cartData;
  }),
  setCartDetail: action((state, cartDetail) => {
    state.userSession.CartDetail = cartDetail;
  }),
  setDiscountArray: action((state, DCArray) => {
    state.userSession.CartArray = DCArray;
  }),
  setCartTotal: action((state, saleTotal) => {
    state.userSession.CartTotal = saleTotal;
  }),
  setCartDCSum: action((state, discountSum) => {
    state.userSession.CartDCSum = discountSum;
  }),
  setShipCost: action((state, fee) => {
    state.userSession.ShipCost = fee;
  }),
  setPayAmount: action((state, sum) => {
    state.userSession.PayAmount = sum;
  }),
  setData: action((state, data) => {
    state.data.data = data;
  }),
  setFilters: action((state, filters) => {
    state.data.filters = filters;
  }),
};

export default model;

Thanks,

jmyrland commented 1 year ago

Hi @lunacer !

I've made a codesandbox with your code for debugging purpouses that illustrates the issue.

Going through your actions, I spottet the loginState action, which re-assigns the whole store state object into state.userSession.isLoggedIn - causing a reference loop. You can see this by pressing the button in the sandbox above.

  // whoops! missing payload 👇 duplicating `state` into `isLoggedIn`
  loginState: action((state) => {
    state.userSession.isLoggedIn = state;
  }),

I'm guessing that this is not what you intended to do, but instead do something like this:

  // Include payload parameter 👇
  loginState: action((state, payload) => {
    // Use payload instead of the state 👇
    state.userSession.isLoggedIn = payload;
  }),

Let me know if this resolves your issue 👍

lunacer commented 1 year ago

Thank YOU so much!! This solved my issue. I completely failed to notice this while I checked the code several times.

jmyrland commented 1 year ago

Thank YOU so much!! This solved my issue. I completely failed to notice this while I checked the code several times.

Happy to help 👍