RangeError: недопустимая длина массива

Я хочу получить массив из ключей моего объекта, но длина должна быть равна 0, когда объект пуст. Длина массива верна, когда я пытаюсь console.info() с массивом, но мой код зависает, выдавая следующую ошибку в моем браузере, и он останавливает выполнение:

RangeError: invalid array length
burger/transformedIngredients<
src/components/Burger/Burger.js:8

   5 | const burger = (props) => {
   6 |   let transformedIngredients = Object.keys(props.ingredients).map(igKey => (
   7 |     // eslint-disable-next-line max-len,react/no-array-index-key
>  8 |     [...Array(props.ingredients[igKey])].map((_, i) => <BurgerIngredient key = {igKey + i} type = {igKey} />)
   9 |   )).reduce((arr, el) => (
  10 |     arr.concat(el)
  11 |   ), []);

Это код, который я использую:

const burger = (props) => {
  let transformedIngredients = Object.keys(props.ingredients).map(igKey => (
    // eslint-disable-next-line max-len,react/no-array-index-key
    [...Array(props.ingredients[igKey])].map((_, i) => <BurgerIngredient key = {igKey + i} type = {igKey} />)
  )).reduce((arr, el) => (
    arr.concat(el)
  ), []);

  if (transformedIngredients.length === 0) {
    transformedIngredients = <p>Please add some ingredients!</p>;
  }

Я прохожу ingredients отсюда:

class BurgerBuilder extends Component {
  state = {
    ingredients: {
      salad: 0,
      bacon: 0,
      cheese: 0,
      meat: 0,
    },
    totalPrice: 4,
  };
render() {
    return (
      <Fragment>
        <Burger ingredients = {this.state.ingredients} />
        <BuildControls
          ingredientAdded = {this.addIngredientHandler}
          ingredientRemoved = {this.removeIngredientHandler}
        />
      </Fragment>
    );
  }
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
0
17 381
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Ответ принят как подходящий

Я действительно не понимаю, почему вы делаете дополнительное сопоставление ключей и создаете многомерный массив, но вы можете изменить свой код на это, если хотите отобразить ингредиент для каждого ключа:

const burger = (props) => {
  let transformedIngredients = Object.keys(props.ingredients).map((igKey, i) => (
    <BurgerIngredient key = {igKey + i} type = {igKey} />
  ));
}

На самом деле, я новичок в React и проходил курс. После копирования вашего кода ошибка исчезла, но мой браузер выдал другую ошибку: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops. Есть ли решение этой проблемы?

user11717245 20.07.2019 13:47

Вы вызываете setState где-то внутри своих методов рендеринга?

Clarity 20.07.2019 13:49

Нет. Вы можете проверить мой полный код здесь

user11717245 20.07.2019 13:53

См. это и это

user11717245 20.07.2019 14:06

Судя по скринам, ошибка возникает из-за метода ingredientHandler, но я не могу найти ее в вашем коде.

Clarity 20.07.2019 14:17

Извините, возникла проблема с git. Вы можете проверить репо сейчас и найти обновленный код

user11717245 20.07.2019 14:25

@shirish хорошо, да, в строке 19 в buildControls вы делаете added = {props.ingredientAdded(ctrl.type)}, который все время вызывает ingredientAdded. Вам нужно изменить его на `add = {() => props.ingredientAdded(ctrl.type)}`. Кстати, имена компонентов React следует начинать с прописных букв.

Clarity 20.07.2019 14:29

Я сделал это для removed = {props.ingredientRemoved(ctrl.type)} тоже, и это сработало. Но я до сих пор не понимаю, в чем была ошибка, можете ли вы объяснить это подробнее или указать мне на некоторые документы?

user11717245 20.07.2019 14:34

В основном props.ingredientRemoved(ctrl.type) выполняется сразу и вызывает повторную визуализацию, что приводит к ее повторному выполнению и вызывает повторную визуализацию и так далее. Обертывание его в функцию предотвратит то, что, поскольку оно будет вызвано сразу, оно вернет другую функцию, которая будет вызвана позже.

Clarity 20.07.2019 14:39

remove = {props.ingredientRemoved(ctrl.type)} это неправильно, если ваш передаваемый параметр, как в этом случае (ctrl.type), вы должны использовать (), например: remove = {() => props.ingredientRemoved(ctrl.type )}

Aniket 15.09.2020 16:08

Ошибка может возникнуть, если один из ингредиентов имеет отрицательное значение, например:

state = {
  ingredients: {
    salad: -1, // this will cause the error
    bacon: 0,
    cheese: 0,
    meat: 0,
  },
  totalPrice: 4,
};

Вы можете предотвратить эту ситуацию, гарантируя, что отрицательное число не будет передано в конструктор Array, например, вы можете передать 0, если число отрицательное:

[...Array(Math.max(0, props.ingredients[igKey]))]

Ни один из моих ингредиентов не будет иметь отрицательного значения. Там ошибка, о которой был вопрос, решена, но теперь я получаю другую ошибку (проверьте комментарии в первом ответе)

user11717245 20.07.2019 13:58

Я прохожу тот же курс и получаю ту же ошибку. Проверьте свой файл reducer.js и сравните его с исходным кодом, предоставленным в курсе. В моем случае массив, переданный из reducer.js, не соответствовал массиву, полученному Burger.js. Ошибка исчезла после исправления файла reducer.js.

Мой код выглядит так после исправления:

    const reducer = (state = initialState, action) => {
  switch (action.type) {
    case actionTypes.ADD_INGREDIENT:
      return {
        ...state,
        ingredients: {
          ...state.ingredients,
          [action.ingredientName]: state.ingredients[action.ingredientName] + 1
        },
        totalPrice: state.totalPrice + INGREDIENT_PRICES[action.ingredientName]
      };
    case actionTypes.REMOVE_INGREDIENT:
      return {
        ...state,
        ingredients: {
          ...state.ingredients,
          [action.ingredientName]: state.ingredients[action.ingredientName] - 1
        },
        totalPrice: state.totalPrice + INGREDIENT_PRICES[action.ingredientName]
      };
    default:
      return state;
    }
    };    

Я учусь реагировать на тот же курс, и у меня была та же проблема, что и у вас. В вашем const INGREDIENT_PRICES все объекты должны иметь тот же регистр, что и в buildcontrols.js, и в этом const элементы управления проверяют верхнюю и нижнюю причину совпадения слов с INGREDIENT_PRICES, после чего проблема будет решена.

в этом случае я сузил свою ошибку до попытки передать значение цены в параметры запроса, установив Number.parseFloat и toFixed для целого числа цен, тем самым устранив ошибку, вызывающую строковое десятичное число.

purchaseContinueHandler = () => {            
            const queryParams = [];
            let price = Number.parseFloat(this.state.totalPrice).toFixed(2);

            for (let i in this.state.ingredients) {
                queryParams.push(encodeURIComponent(i) + '=' + encodeURIComponent(this.state.ingredients[i]));
            }
            queryParams.push('price=' + price);

            const queryString = queryParams.join('&'); 

            this.props.history.push({
                pathname: '/checkout',
                search: '?' + queryString
            });

ошибка была вызвана для меня наличием десятичной строки в значении цены.

Имеет ту же ошибку, что и у вас, возможно, ошибка была в добавлении ингредиентов.

Решил мою, изменив «[]» на «{}» в строке копирования объекта. Я не заметил, что использовал [].

Я тоже сделал ту же ошибку, заменив буквы "b" и "c" на заглавные. в "беконе" и "сыре".

Сделав каждый символ в одном и том же регистре, проблема решена:

const controls=[
  {label:'salad',type:'salad'},
  {label:'bacon',type:'bacon'},
  {label:'cheese',type:'cheese'},
  {label:'meat',type:'meat'}
  ];

Я следую тому же курсу, пожалуйста, проверьте название ингредиента, которое вы используете в своей базе данных, оно должно быть таким же, как в BuildControls.js, тип ингредиентов должен быть таким же, как вы пишете в своей базе данных. Это определенно решит вашу проблему.

Эта ошибка возникает из-за выбора неправильного имени, поэтому пишет Недопустимая длина массива.

Спасибо всем, кто нашел время, чтобы предоставить свой ответ и мысли. У меня была такая же проблема, и решение состояло в том, чтобы просто изменить значение ключа на нижний регистр,

Например, в приведенном ниже коде: Ошибка заключалась в том, что 'Price' в верхнем регистре вместо 'price' в нижнем.

checkoutContinuedHandler = () => {
        const queryParams = [];
        for (let ing in this.state.ingredients) {
            queryParams.push(encodeURIComponent(ing) + '=' + encodeURIComponent(this.state.ingredients[ing]));
        }
        queryParams.push('price=' + this.state.totalPrice.toFixed(2));
        const queryString = queryParams.join('&');
        this.props.history.push({
            pathname: '/checkout',
            search: '?' + queryString
        });
    }

Я следовал тому же курсу и столкнулся с той же проблемой. Включено totalPrice как пара ключ-значение в объект ингредиентов (в файле reducer.js), что не так, поскольку это отдельная пара ключ-значение в обновленном состоянии для каждого действия.

Перед исправлением-

import * as actionTypes from './actions';

const initialState = {
    ingredients: {
        salad: 0,
        bacon: 0,
        cheese: 0,
        meat: 0
    },
    totalPrice: 4,
};

const INGREDIENT_PRICES = {
    salad: 0.5,
    bacon: 1.3,
    cheese: 0.6,
    meat: 1,
};

const reducer = (state = initialState, action) => {
    switch(action.type){
        case actionTypes.ADD_INGREDIENT: return {
            ...state,
            ingredients: {
                ...state.ingredients,
                [action.ingredientName]: state.ingredients[action.ingredientName] + 1,
                `totalPrice: state.totalPrice + INGREDIENT_PRICES[action.ingredientName]`
            }
        };

        case actionTypes.REMOVE_INGREDIENT: return {
            ...state,
            ingredients: {
                ...state.ingredients,
                [action.ingredientName]: state.ingredients[action.ingredientName] - 1,
                `totalPrice: state.totalPrice - INGREDIENT_PRICES[action.ingredientName]`
            }
        }

        default : return state;
    }
};

export default reducer;

После исправления-

import * as actionTypes from './actions';

const initialState = {
    ingredients: {
        salad: 0,
        bacon: 0,
        cheese: 0,
        meat: 0
    },
    totalPrice: 4,
};

const INGREDIENT_PRICES = {
    salad: 0.5,
    bacon: 1.3,
    cheese: 0.6,
    meat: 1,
};

const reducer = (state = initialState, action) => {
    switch(action.type){
        case actionTypes.ADD_INGREDIENT: return {
            ...state,
            ingredients: {
                ...state.ingredients,
                [action.ingredientName]: state.ingredients[action.ingredientName] + 1,
            },
            `totalPrice: state.totalPrice + INGREDIENT_PRICES[action.ingredientName]`
        };

        case actionTypes.REMOVE_INGREDIENT: return {
            ...state,
            ingredients: {
                ...state.ingredients,
                [action.ingredientName]: state.ingredients[action.ingredientName] - 1,
            },
            `totalPrice: state.totalPrice - INGREDIENT_PRICES[action.ingredientName]`
        }

        default : return state;
    }
};

export default reducer;

Просто удалите оператор спреда из ...state.ingredients, и все заработает!

Другие вопросы по теме