7 лет назал я защитил докторскую диссертацию по социологии, а год назад я вышел на работу как программист.
пятница, 11 февраля 2022 г.
понедельник, 4 января 2021 г.
A programmer: JavaScript #29. Некоторые пробелы в образовании в React
Когда я больше месяца назад взялся сделать тестовое заданиепо MERN, то по неопытности своей сказал, что сделаю меньше, чем за неделю. Не справился и за полторы, потому что постоянно вылазили какие-то не первый взгляд не слишком принципиальные пробелы в образовании, которые оказывались критическими в каждый данный момент времени. Разбираюсь с ними до сих пор, часто - с советами и помощью от добрых людей. Так, например, пагинацию (вывод данных постранично) делал больше недели, а затыков было несколько:
Во-первых, не знал, как передавать параметр в обработчик события. Во-вторых, делал setState асинхронным, хотя он предполагает колбек. В итоге посоветовали сделать так:
setCurrentPage = p => e => this.setState({ currentPage: p }, this.registerHandler);
Я такой записи не видел раньше, но пришлось вчитаться в документацию ещё раз (тут и тут):
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
У меня:
onClick={this.setCurrentPage(p)}
Таки да - как видим, аргументом передается параметр, а setState колбеком вызывает то, что и нужно было выполнить после изменения state.
В-третьих, был затык с тем, что я сравнивал число со строкой, не замечая этого, и для того, чтобы всё работало, как надо, надо было делать приведение типов:
parseInt(this.state.currentPage) === p
При этом это эквивалентно, если вместо parseInt просто поставить "+" - этого я точно нигде не встречал.В четвертых, забыл, что в map() надо указывать уникальный ключ - поэтому при правильно работающем переключении страниц и запросах, отрисовывалось то же, что и было, потому что ключи не менялись.
Итого: Сейчас я писал через классы и без внешнего стейта, значит, надо переписать - 1) сделать внешний стейт, 2) классы заменить на функциональные компоненты и сделать всё через хуки, 3) разделить презентацию и бизнес-логику.
суббота, 26 декабря 2020 г.
A programmer: JavaScript #28. Routing in React
Некоторое время назад делал я тестовое задание по стеку MERN (MongoDB, ExpressJS, React, NodeJS). Сделать вовремя не сумел, но добрые люди согласились мне помочь доделать его - для себя, просто чтобы разобраться. Важный комментарий был мне дан по поводу того, что при маршрутизации моя программа перезагружает страницу, чего не должно быть, поскольку в этом случае теряется вся логика Single Page Application, для чего, собственно, и разработан React. Логика, напомню, состоит в том, что вместо загрузки необходимых страниц с сервера по каждому запросу, должно загружаться всё и сразу (кроме баз данных), и в зависимости от запроса - отрисовываться на клиенте то, что нужно, но без перезагрузки страницы. И понял я, что с маршрутизацией в React'e мне, оказывается, ещё нужно разобраться получше.
Итак, для маршрутизации в React'e существует модуль react-router-dom, который предоставляет несколько ключевых инструментов:
BrouserRouter (обычно импортируемый как просто Router) - модуль, в котором, собственно, должна быть описана маршрутизация.
Router - в сущности, аналог href - в нем прописывается путь, в зависимости от которого должно быть отрисовано нужное содержание.
Если кроме непосредственных ссылок надо обработать отсутствующую страницу, то Route'ы надо обернуть в Switch.
Кроме того, можно использовать Redirect, Link и NavLink (который отличается от Link тем, что в нем можно ещё и ссылаться не описание стилей для линков).
Так вот, в моем случае надо было использовать Link вместо Route - в этом случае страница не перезагружается, и маршрутизация осуществляется на клиенте, в противном случае идет запрос на сервер:
<Link to="/"><button type="button">Home</button></Link>
пятница, 18 декабря 2020 г.
A programmer: JavaScript #27. Hooks in React
Поскольку, грубо говоря, в JavaScript всё есть объект, то и компоненты React тоже вполне логично интерпретируются как объекты. А у объектов, как известно, есть состояние (state) и есть поведение. Состояние хранится в самом объекте, и его можно в нем же менять через this.setState. Но в последнее время разработчики React стараются отходить от такой практики и использовать компоненты как "функциональные компоненты", или просто функции. А функции не имеют состояния (stateless). Но состояние - вещь необходимая, и возникшую проблему надо решать - для этого и придумали хуки (hooks). Хук - это функция, которая меняет состояние, которое хранится в локальной переменной.
Классический пример - использование хука useState для счетчика (сначала его надо импортировать и библиотеки 'react'):
const [count, setCount] = useState(0);
Здесь происходит "деструктуризация массива" - объявляются и переменная состояния (count), и собственно хук (setCount). То есть запись эквивалентна следующей:
var countStateVariable = useState(0);
var count = countStateVariable[0]; // Объявление
переменной состояния
count = 0; // Присваивание начального значения
var setCount = сountStateVariable[1]; // Объявление хука
Теперь к ним можно обращаться в рендере:
button onClick={() => setCount(count + 1)}
Другой классический пример – хук useEffect. Он определяет действия, которые должны быть выполнены после каждого рендера – то есть заменяет собой встроенные методы, которые связанны с жизненным циклом компонента (componentDidMount, componentDidUpdate и др.). Поскольку перерисовка компонента обычно связана с изменением состояния (за которое обычно отвечает useState), то часто оба эти хука используются вместе:
const [count, setCount] = useState(0);
useEffect(() => { document.title = `Вы нажали ${count} раз` });
В данном случае document.title будет перерисовываться при каждом изменении состояния.
Существуют и другие хуки, а также можно написать собственные.
среда, 16 декабря 2020 г.
A programmer: JavaScript #26. A conditional statement in React
Некоторое время назад мучился с небольшой проблемой: вроде бы программа работала правильно, но в консоли браузера откуда-то выскакивало сообщение "GET http://localhost:3000/undefined". Расследование показало, что это связано с тем, что я добавляю в базу данных объект без картинки, при этом у меня должен отрисовываться элемент, который имеет свойство
backgroundImage: `url(${this.state.picture})`
То есть, когда картинки нет, он выбрасывает undefined, что логично. Мне посоветовали написать так:
this.state.picture && { backgroundImage: `url(${this.state.picture})` }
"Странная запись" - подумал я, имея в виду оператор "И", и пошел разбираться. Оказалось, что всё логично: React читает эту строчку последовательно слева направо. Если картинка в state есть, то он И отрисовывает её, а если её нет, то на операторе "И" прекращает читать строку, потому что вне зависимости от того, что дальше, значение будет false, и до запроса на вывод картинки дело не доходит.
вторник, 15 декабря 2020 г.
A programmer: JavaScript #25. Widdleware in ExpressJS
Тема, которая почему-то вообще не попадалась мне на глаза раньше, но оказалась полезной и достаточно несложной. Напомню, что ExpressJS - это фреймворк для более простой работы с вебсервером. Грубо говоря, если в "ванильном" варианте надо было работать с XMLHttpRequest-объектом (XHR) NodeJS'a, то в ExpressJS создается свой объект app, через каторый и идет взаимодействие с сервером. Так вот, маршрутизация предполагает, что серверная часть имеет эндпойнты (например, "/api"), при обращении к которым выполняются некоторые действия. Например:
app.get("/api", (req, res) => {
// code
})
Так вот, существует возможность указать некоторые функции, которые должны быть выполнены ПЕРЕД выполнением данного года. Для этого определяется некоторая функция, кроме req и res она принимает параметр next, который вызывается в конце этой функции, чтобы передать выполнение следующему middleware или собственно основному коду (если надо выдать ошибку, она передается как параметр внутри next(error), никакой другой параметр там передаваться не может). Например:
const middleware = (req, res, next) => {
// code
next();
}
Чтобы подключить middleware, надо указать:
app.use("/api", middleware);
Если указать без указания на рут, middleware будет "подключено" глобально, то есть будет выполняться в любом руте:
app.use(middleware);
Ну и, разумеется middleware, как и любой кусок кода можно импортировать извне, а внутри только подключать.
вторник, 24 ноября 2020 г.
A programmer: JavaScript #24. AJAX, XMLHttpRequest, fetch(), async/await
Когда я первый раз узнал об AJAX-запросах, это было связано с необходимостью получать данные с сервера, не перезагружая страницу. И выглядело это приблизительно так:
Что означало: при нажатии кнопки на клавиатуре в поле "login", взять данные и передать их методом POST в обработчик ajax.php (название файла, разумеется, может быть любым - скрин с php-проекта), и в случае успешной отработки обработчика, вывести результат на страницу в элемент с названием "#login_warning".
И, собственно, вся асинхронность у меня стала после этого ассоциироваться с тем, что оказалось всего-лишь частью библиотеки jQuery. Но принципиально - это только частный случай Asynchronous JavaScript And XML (AJAX). О чем идет речь?
Ранее я говорил, что promise - это способ справиться с асинхронностью - сделать так, чтобы команды выполнялись последовательно, поскольку часто бывает так, что для выполнения следующей функции нужен результат предыдущей, а в JavaScript это не гарантировано. Чаще всего это нужно при отправке http-запросов, когда данные должны быть выведены после получения ответа от сервера.
Без promise'ов это коротко выглядит так: создается специальный объект XMLHttpRequest, не нем открывается соединение и посылается запрос. Заранее определяется, что должно выполниться при том или ином ответе сервера:
fetch() - это интерфейс получения запросов - то есть способ сделать то же самое. "Под капотом" он создает объект request'a, посылает его, но возвращает promise с обработкой response'а:
На самом деле там вставляются ещё проверки, формат вывода данных, а в случае POST-запроса ещё и дополнительные параметры, но принцип таков.
Ну и async/await - это приблизительно то же самое. async перед функцией указвает на то, что функция возвращает promise, соответственно, внутри функция будет ждать результата от promise'a (в этом месте пишется await). Ну и поскольку promise может завершиться с ошибкой, то всё это оборачивается в try-catch:
Информация - отсюда, отсюда, отсюда, отсюда, отсюда. На самом деле ещё много откуда, но это самые последние по времени использования ресурсы.
UPD:
1. Нашел ещё один очень удобный ресурс - вот он. Здесь показаны разные варианты работы с асинхронными запросами в одном видео.
2. Про популярный нынче axios ничего не писал, но там логика приблизительно такая же.