пятница, 11 февраля 2022 г.

I am a software developer

7 лет назал я защитил докторскую диссертацию по социологии, а год назад я вышел на работу как программист.

понедельник, 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 ничего не писал, но там логика приблизительно такая же.