#reactjs #react-hooks
#reactjs #реагирующие крючки
Вопрос:
Для удобства чтения я собираюсь убрать большую функциональность из моих примеров. Однако, по сути, у меня есть useEffect
(показано ниже) зависимость, которая отслеживает state.cards
массив объектов card. Я предполагал, что если это state.cards
свойство изменится, то useEffect
должно сработать. Однако, это не совсем так.
Ниже приведены два решения, которые используются. Я хочу использовать первый, поскольку он находится в постоянном времени. Второй, хотя и точный, является линейным. Что меня смущает, так это то, почему второй вариант запускает зависимость, а первый — нет. Оба возвращают клон корректно измененного состояния.
Это не запускает зависимость от эффекта использования state.cards
.
const newArr = { ...state };
const matchingCard = newArr.cards[action.payload]; <-- payload = number
matchingCard.correct = 1;
matchingCard.lastPass = true;
return newArr;
Это действительно запускает зависимость от эффекта использования state.cards
.
const newArr = { ...state };
const cards = newArr.cards.map((card) => {
if (card.id === action.payload.id) {
card.correct = 1;
card.lastPass = true;
}
return card;
});
return { ...newArr, cards };
Эффект использования
useEffect(() => {
const passedCards = state.cards.filter((card) => {
return card.lastPass;
});
setLearnedCards(passedCards);
const calculatePercent = () => {
return (learnedCards.length / state.cards.length) * 100;
};
dispatch({ type: 'SET_PERCENT_COMPLETE', payload: calculatePercent() });
}, [learnedCards.length, state.cards]);
Состояние
const initialState = {
cards: [], <-- each card will be an object
percentComplete: 0,
lessonComplete: false,
};
Решение: Рабочее решение с использованием первого примера:
const newCardsArray = [...state.cards];
const matchingCard = newCardsArray[action.payload];
matchingCard.correct = 1;
matchingCard.lastPass = true;
return { ...state, cards: newCardsArray };
Почему: Расширение массива state.cards
создает новую мелкую копию этого массива. Затем я могу внести изменения в этот клонированный массив и вернуть его как новое значение, присвоенное state.cards
. У расширенного массива есть новая ссылка, и это обнаружено useEffect
.
Комментарии:
1. На что
state
похоже?2. @Yousaf обновлен с исходным состоянием
3. Проблема в первом примере кода заключается в том, что он изменяет состояние напрямую.
{ ...state }
— Это не приведет к клонированию объектов внутриstate
. Это означает, что вы изменяетеcards
массив напрямую. Это не изменяетstate.cards
, следовательноuseEffect
, не запускается. Способ сделать это — сопоставление надstate.cards
.4.
{ ...state }
— Это создание нового объекта, копирование свойствstate
в новый объект, но объекты являются ссылочными типами, поэтому вы в основном создаете свойство с именемcards
в новом объекте, но его значением является та же ссылка на массив, на которую указывалоcards
свойство вstate
объекте.5. @Kevin достаточно справедливо. У меня такое чувство, что он улавливает правильное значение при повторном запуске. Вы могли бы просто использовать
passedCards.length
для передачи правильного значения в том же цикле useEffect.
Ответ №1:
Мое лучшее предположение заключается в том, что во втором рабочем примере .map возвращает новый массив с новой ссылкой. В первом примере вы просто изменяете содержимое массива, но не ссылку на этот массив.
Я не совсем уверен, как сравнивается useEffect, но, если я правильно помню для объекта, все дело в ссылке на этот объект. Что иногда затрудняет использование useEffect для объектов. То же самое может быть и с массивами.
Почему бы вам не попробовать:
const newCardsArray = [...state.cards]
// do your mutations here
следует скопировать массив с новой ссылкой, как вы сделали с объектом.
Комментарии:
1. «ключи» в массиве? Ваш комментарий не имеет большого смысла, или мне не хватает деталей, которые могли бы вам помочь. Если вы хотите принудительно выполнить обновление, обновите ссылку, которую вы передаете в качестве поддержки другому компоненту. Приведенный выше пример делает это для массивов