Содержание урока:
Материалы к изучению:
- одинаковый код в наблюдателях
- при удалении остается подписка (таймер)
- количество запросов (в АПИ есть возможность получить данные по списку тикеров)
- запросы к АПИ внутри компонента
- обработка ошибок АПИ
- наличие состояния зависимых данных
- график переполняется
- при удалении тикера не меняется LocalStorage
- LocalStorage и анонимные вкладки (LocalStorage может быть недоступен)
- магические строки и числа (url, 5000 мсек, ключ localstorage, размер страницы)
Параллельно
- график сломан, если везде одинаковые значения
- при удалении тикера остается выбор
Метод filteredTickers зависит от страниц и массива тикеров, в таком случае нужно использовать вычисляемые свойства
В Composition API у вычисляемых свойств другой синтаксис:
const calculatedProperty = computed(() => {
// тут какие-то вычисления на основе состояний
return result
})
Вычисляемые свойства кешируются, т.е. будут вычисляться только при изменнии используемых реактивных переменных (состояний). В нашем случае это "номер страницы", "строка фильтра" и "список тикеров"
При использовании в шаблоне надо помнить, что computed это свойство, а не функция. Он не может принимать аргументы (не нужно указывать круглые скобки)
Если установлен линтер, то он может показать ошибку, что присваиванию hasNextPage не место в computed
Т.к. это тоже вычисляемое свойство, то его тоже можно завернуть в computed
Попутно решили, что start и end тоже можно вынести в computed для единообразия
В итоге получились такие вычисляемые свойства:
const startIndex = computed(() => {
return (page.value - 1) * 6
})
const endIndex = computed(() => {
return page.value * 6
})
// это объясняется чуть позже
const filteredTickers = computed(() => {
return tickers.value.filter(
t => t.name.includes(filter.value.toUpperCase()))
})
// не забудьте поменять в шаблоне filteredTickers на paginatedTickers
const paginatedTickers = computed(() => {
return filteredTickers.value.slice(startIndex.value, endIndex.value)
})
// не забудьте удалить переменную hasNextPage
const hasNextPage = computed(() => {
// я уже упоминал, что computed возвращает реактивный объект, т.е. к его значению надо обращаться через свойство .value
return filteredTickers.value.length > endIndex.value
})
Как понять что нужно использовать computed? Очень просто. Если нет прямого изменения состояния (присваивания), а только вычисление по другим реактивным переменным (в лекции сказали ещё "если результат используется в шаблоне", но это не так, в этом же примере startIndex и endIndex сделаны вычисляемыми, но они используются только в коде).
Смотрим, что ещё можно поменять на computed?
-
normalizeGraph - переделайте в computed самостоятельно, заодно переименуйте в normalizedGraph (прилагательное вместо глагола, т.е. у нас не действие, а свойство)
-
разбить filteredTickers на два вычисляемых свойства (выше уже есть код)
Если минимальное значение равно максимальному, то возвращать 50%, чтобы было ровное плато посередине.
const normalizedGraph = computed(() => {
const maxValue = Math.max(...graph.value)
const minValue = Math.min(...graph.value)
if (maxValue == minValue) {
return graph.value.map(() => 50)
}
return graph.value.map(
price => 5 + ((price - minValue) * 95) / (maxValue - minValue)
)
})
В коде часто встречаются условия "если изменилось что-то, то сделай то-то". Это значит, что можно использовать watch
Рассматривается пример с удалением тикеров на второй странице. В результате мы остаемся на пустой странице.
Вариантов решения два:
-
в методе удаления тикеров проверить длину массива paginatedTickers и если он пустой, то уменьшить номер страницы
-
использовать наблюдатели
Обратите внимание, я использую синтаксис с параметрами. Первым параметром в функции приходит новое значение переменной.
watch(paginatedTickers, (value) => { if (value.length === 0 && page.value > 1) page.value-- })
В метод handleDelete добавляем логику проверки и очистки выделенного тикера
if (sel.value == tickerToRemove) {
sel.value = null
}
Заодно поменяйте sel на человеко-понятное наименование (названия переменных должны быть самоочевидны)
Напоминаю текущий код:
function select (ticker) {
sel.value = ticker
graph.value = []
}
Он соответствует правилу "если изменилось что-то, то сделай то-то", соответственно можно его перенести очистку графика в наблюдатель (сделайте самостоятельно)
Вынос логики сохранения тикеров в LocalStorage в наблюдатель (заодно исправится бага, когда список не сохранялся при удалении тикера)
Наблюдатель реализуйте сами. И не забудьте изменить добавление тикеров в массив (иначе на сработает watch на массив)
tickers.value = [...tickers.value, newTicker]
Что делает этот код?
Выражение ...array
делает декомпозицию массива, т.е. извлекает все его элементы и вставляет в новый массив. В итоге в массив тикеров записывается новый массив, содержащий все старые элементы и новый.
-
Создаем вычисляемое свойство pageStateOptions, которое будет хранить и фильтр и страницу:
const pageStateOptions = computed(() => { return { filter: filter.value, page: page.value } })
-
И вешаем на него наблюдателя
watch(pageStateOptions, (value) => { window.history.pushState( null, document.title, `${window.location.pathname}?filter=${value.filter}&page=${value.page}` ) })
Задание