Translate

Поиск по этому блогу

Показаны сообщения с ярлыком JavaScript. Показать все сообщения
Показаны сообщения с ярлыком JavaScript. Показать все сообщения

среда, 17 июня 2020 г.

Ошибки при работе c React state.

Сегодня мы поговорим об основных, часто встречающихся ошибках, которые часто допускают разработчики при работе с состоянием (state) при разработке React-приложения.



Одна из моих любимых вещей в веб-разработке - всегда есть что-то новое для изучения. Вы могли бы посвятить всю свою жизнь освоению различных языков программирования, библиотек и фреймворков и все еще не знать всего этого.

Поскольку мы все учимся, это также означает, что мы все также склонны к ошибкам. Хорошо. Цель - стать лучше и стать лучше. Если ты ошибаешься и учишься на ней, у тебя все отлично! Но если вы не научились чему-то новому и продолжаете повторять одни и те же ошибки, ну ... тогда, похоже, вы застаётесь в своей карьере.

В этом духе, вот три распространенных ошибки, которые я часто вижу во время обзоров кода, которые младшие разработчики делают при работе с состоянием компонента React. Мы рассмотрим каждую ошибку и затем обсудим, как ее исправить.

  1. Изменение состояния напрямую.

  2. При изменении состояния компонента важно, чтобы вы возвращали новую копию состояния с изменениями, а не изменяли текущее состояние напрямую. Если вы неправильно измените состояние компонента, алгоритм сравнения React не поймает изменение, и ваш компонент не будет обновляться должным образом. Давайте посмотрим на пример.

    Скажем, у вас есть состояние, которое выглядит так:

    this.state = {
      colors: ['red', 'green', 'blue']
    }
    


    И теперь вы хотите добавить цвет «yellow» в этот массив. Может быть заманчиво сделать это:

    this.state.colors.push('yellow')
    


    Или даже это:

    this.state.colors = [...this.state.colors, 'yellow']
    


    Но оба эти подхода неверны! При обновлении состояния в компоненте класса вам всегда нужно использовать метод setState, и вы всегда должны быть осторожны, чтобы не изменять объекты. Вот правильный способ добавить элемент в массив:

    this.setState(prevState => ({ colors: [...prevState.colors, 'yellow'] }))
    


    И это приводит нас прямо к ошибке номер два.

  3. Установка состояния, которое опирается на предыдущее состояние без использования функции

  4. Есть два способа использовать метод setState.

    Первый способ - предоставить объект в качестве аргумента.

    Второй способ - предоставить функцию в качестве аргумента.

    Итак, когда вы хотите использовать один поверх другого?

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

    this.setState({ isDisabled: !this.state.isDisabled })
    


    Итак, что с этим не так? Проблема заключается в том, что обновления состояния React могут быть пакетными, а это означает, что в одном цикле обновления может происходить несколько обновлений состояния. Если ваши обновления должны были быть пакетными, и у вас было несколько обновлений до включенного / отключенного состояния, конечный результат может быть не тем, что вы ожидаете.

    Более правильным способом обновления состояния здесь было бы предоставление функции предыдущего состояния в качестве аргумента:

    this.setState(prevState => ({ isDisabled: !prevState.isDisabled }))
    


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

    То же самое верно для чего-то вроде увеличения счетчика.

    Не делайте этого!!!:

    this.setState({ counterValue: this.state.counterValue + 1 })
    


    Делайте правильно!!!:

    this.setState(prevState => ({ counterValue: prevState.counterValue + 1 }))
    


    Ключевым моментом здесь является то, что если ваше новое состояние зависит от значения старого состояния, вы всегда должны использовать функцию в качестве аргумента. Если вы устанавливаете значение, которое не зависит от значения старого состояния, то вы можете использовать объект в качестве аргумента.


  5. Не забывайте, что setState является асинхронным

  6. Наконец, важно помнить, что setState является асинхронным методом. В качестве примера, давайте представим, что у нас есть компонент с состоянием, которое выглядит так:

    this.state = { name: 'John' }
    


    И затем у нас есть метод, который обновляет состояние и затем выводит состояние на консоль:

    this.setState({ name: 'Matt' })
    console.log(this.state.name)
    


    Вы можете подумать, что вывод будет 'Matt' на консоль, но это не так! Это будет - «Джон»!

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

    Если у вас есть код, который необходимо запустить после обновления состояния, React позволяет предоставить функцию обратного вызова, которая запускается после завершения обновления.

    Правильный способ записать текущее состояние после обновления:

    this.setState({ name: 'Matt' }, () => console.log(this.state.name))
    


    Намного лучше! Теперь вывод в консоль будет 'Matt', как и ожидалось.
Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.


Удачного кодирования!

Оригинал можно посмотреть здесь: Tyler Hawkins                                                                                                                                                              

Телеграм канал - Full Stack JavaScript Developer
Помочь проекту (любая валюта). DONATE

воскресенье, 26 апреля 2020 г.

Callback functions в JavaScript.

В этой статье мы познакомимся с функциями обратного вызова в JavaScript на нескольких примерах. Функции обратного вызова также называют функциями высшего порядка.



Функция высшего порядка — это функция, которая может принимать другую функцию в качестве аргумента или возвращать другую функцию в качестве результата.

Функция в JS является объектом первого класса.

В языках программирования существует понятие "объекты первого рода (или класса)". Им обозначают элементы, которые могут быть переданы в функции, возвращены из функций и присвоены переменным (или константам). К таким элементам относятся любые данные, например числа, строки, массивы или логические значения. Обратите внимание, что сама переменная (или константа) под это понятие не попадает, объектом первого рода считаются те данные, которые лежат в переменной (или константе).

  1. Мы можем передать объект в функцию в качестве аргумента.
  2. Мы также можем передать другие функции в функцию в качестве аргумента для выполнения в некоторый момент.


Простой пример для понимания:



    let function1 = function(){
        console.log("I'm the function1 result");
    }
    
    
    let function2 = function(callback){
        console.log("I'm the function2 result");
        
        callback();
    }
    
    
    function2(function1);

// I'm the function2 result
// I'm the function1 result


Всё это работает нормально. Легко понять, как передать функцию в качестве аргумента другой функции.

Но зачем нам этот обратный вызов в JavaScript?

Чтобы лучше понять, посмотрите на приведенный ниже пример, который не использует функции обратного вызова, и посмотрите, насколько это уродливо.

 let calculate = function(x, y, operation){
      
      if(operation === 'subtract')
          return x - y;
    
      else if(operation === 'divide')
          return x/y;
  }
  
  console.log(calculate(10,3,'subtract'));


 // Output:
 //  7


Это отлично работает. Но допустим, что эта функция вычисления является частью некоторой библиотеки, например, библиотеки jQuery. И пользователь должен использовать эту библиотеку, должен передать число и тип расчета, чтобы получить результат.

Теперь для всех возможных типов вычислений, эта библиотека должна реализовать все это. Мы можем передать add, кратно или это могут быть случайные операции. Но эта библиотека поддерживает только два типа вычислений: вычитание и деление.

Так что делать? какой лучший способ для этого?

Лучший способ сделать это - сделать функцию как можно более абстрактной. Чтобы сделать функцию абстрактной, нам нужно вывести функциональность наружу. Функция должна быть общей.

Посмотрите на приведенный ниже пример, чтобы лучше понять.

Используя функции обратного вызова, мы собираемся улучшить приведенный выше код. Так что вы можете увидеть реальное использование функций обратного вызова.

let add = function (x, y) {
  return x + y;
};

let substract = function (x, y) {
  return x - y;
};

let multiplay = function (x, y) {
  return x * y;
};

let divide = function (x, y) {
  return x / y;
};

let calculate = function (x, y, callback) {
  if (typeof callback === "function") return callback(x, y);
};

console.log(calculate(12, 8, add));
// 20


Как пользователь, я могу создать любую функцию, перейти в эту библиотеку и получить желаемый результат. Итак, это сила функций обратного вызова.

Я также могу сделать это,

let calculate = function (x, y, callback) {
  if (typeof callback === "function") return callback(x, y);
};

console.log(
  calculate(12, 8, function (x, y) {
    return x >= y;
  })
);
// true


Исходя из моих требований, я могу изменить все, что смогу. Этот пример выше возвращает true.

Пример из реальной жизни:

Одним из примеров функции обратного вызова JavaScript в реальном времени является метод sort().

var simpleArray = [
  {
    key: 12,
    value: "Hello",
  },
  {
    key: 13,
    value: "World",
  },
  {
    key: 14,
    value: "Something",
  },
];

simpleArray.sort(function (val_1, val_2) {
  if (val_1.key < val_2.key) {
    return -1;
  } else {
    return 1;
  }
});

let result = simpleArray.forEach((res) =>
  console.log(res.key + " " + res.value)
);
// 12 Hello
// 13 World
// 14 Something


В приведенном выше примере мы применили функцию обратного вызова к методу сортировки JavaScript, чтобы отсортировать массив по ключам в порядке возрастания.

Давайте изменим функцию обратного вызова для сортировки массива по ключам в порядке убывания.

simpleArray.sort(function (val_1, val_2) {
  if (val_1.key > val_2.key) {
    return -1;
  } else {
    return 1;
  }
});

let result = simpleArray.forEach((res) =>
  console.log(res.key + " " + res.value)
);
// 14 Something
// 13 World
// 12 Hello


Давайте изменим функцию обратного вызова для сортировки массива на основе значений в порядке возрастания.

simpleArray.sort(function (val_1, val_2) {
  if (val_1.value < val_2.value) {
    return -1;
  } else {
    return 1;
  }
});
// 12 Hello
// 14 Something
// 13 World


Давайте изменим функцию обратного вызова для сортировки массива по значениям в порядке убывания.

simpleArray.sort(function (val_1, val_2) {
  if (val_1.value > val_2.value) {
    return -1;
  } else {
    return 1;
  }
});
// 13 World
// 14 Something
// 12 Hello


Приведенные выше примеры, помогают не только понять работу обратных вызовов в JavaScript, но помогают написать некоторые функции сортировки, а это в свою очередь полезно тем, что не нужно "тянуть" в проект всю библиотеку (например Lodash), только ради простой сортировки.

Я надеюсь, что эта статья поможет вам лучше понять функции обратного вызова в JavaScript с помощью простых примеров.

Таким образом, вы можете сами написать для своих проектов свою собственную библиотеку и использовать ее в виде подключаемого модуля. Как это сделать? Смотрите мои статьи в этом блоге в разделе

JAVASCRIPT&PHP                                                                                                                                                              

Телеграм канал - Full Stack JavaScript Developer

четверг, 31 января 2019 г.

JS ES6 Arrays.

Массивы (Arrays) — лаконичным способом хранения списка элементов под одним именем.


Больше материалов по JavaScript ES6 в этом блоге.

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


let names = ["Steve", "Bob", "Sam", "Erick"]

names[0] //returns "Steve"
names[2] //returns "Sam"



Массивы начинают считать с 0.
В приведенном выше примере "Steve" является первым элементом в массиве имен. Когда мы вызываем names[0], мы просим вернуть элемент в позиции 0 массива. Вот почему names[0] возвращает "Steve", а names[2] возвращает "Sam".

Определение массивов - Defining Arrays.

Есть несколько способов «создать» или инициализировать массивы в JavaScript.
Самый простой способ - инициализировать массив в определении переменной.
Например:


let scores = [87,43,88,99]



Массивы также могут быть созданы с помощью объекта Array:


let scores = new Array(87, 43, 88, 99)



Доступ к элементам массива - Accessing Array Elements

Доступ к элементам в массиве прост.
Вы просто предоставляете имя массива с индексом, который хотите вернуть:


let scores = [87,43,88,99]

scores[0] // returns 87
scores[1] // returns 43
scores[2] // returns 88



Перебор массива циклом.

Чтобы перебрать каждый элемент массива, вы можете использовать встроенный метод forEach ().


let scores = [87,43,88,99]

scores.forEach((x) => {
  console.log(x)
})

//logs 87, 43, 88, 99



Другие методы массива.

Метод forEach () является одним из многих поддерживаемых методов для массивов JavaScript. Ниже приведен список других методов с краткими примерами для каждого:

concat()

Метод concat () объединяет массивы.


let scores = [87, 43, 88, 99]
let moreScores = [100,33,78,44]

scores.concat(moreScores)

//returns [87, 43, 88, 99, 100, 33, 78, 44]



every()

Метод every () возвращает true, если каждый элемент проходит заданное условие:


let scores = [87, 43, 88, 99]

scores.every((x) => { return x > 10 })

//returns true since every element is greater than 10



filter()

Метод filter () возвращает новый массив с каждым элементом, который пройдет через функцию фильтра.


let scores = [87, 43, 88, 99]

scores.filter((x) => { return x > 80 })

//returns [87, 88, 99]



indexOf()

Этот метод возвращает первый элемент массива, который соответствует заданному значению. Если нет элементов соответствующих значению, возвращается -1:


let scores = [87, 43, 88, 99]

scores.indexOf(88) //returns 2
scores.indexOf(103) // returns -1



join()

Метод join () объединяет все элементы массива в строку.


let scores = [87, 43, 88, 99]

scores.join() //returns "87,43,88,99"
scores.join("|") //returns "87|43|88|99"



lastIndexOf()

Аналогичен indexOf, но возвращает индекс последнего элемента, который соответствует предоставленному значению. Возвращает -1, если совпадений нет:


let scores = [87, 43, 88, 99, 88]

scores.lastIndexOf(88) //returns 4
scores.lastIndexOf(11) //returns -1



map()

Функция map () применяет блок кода или функцию к каждому элементу в массиве и возвращает новый массив с результатами каждой операции.


let scores = [87, 43, 88, 99]

scores.map((x) => {return x + 10})

//returns [97, 53, 98, 109]



pop()

Метод pop () удаляет последний элемент в массиве и возвращает этот элемент.


let scores = [87, 43, 88, 99]

scores.pop() //returns 99

//scores now equals [87, 43, 88]



push()

Метод push () добавляет новый элемент в массив и возвращает новую длину массива.


let scores = [87, 43, 88, 99]

scores.push(44) //returns 5

//scores now equals [87, 43, 88, 99, 44]



reduce()

Метод reduce () возвращает агрегированный результат функции, примененной к массиву


let count = [1,2,3,4]

count.reduce((a,b) => {return a + b})

//returns 10



reduceRight()

То же, что и reduce (), но работает справа налево:


let count = [1,2,3,4]

count.reduceRight((a,b) => {return a - b})

//returns -2



reverse()

Повернет в другую сторону элементы в массиве:


let scores = [87, 43, 88, 99]

scores.reverse()

//returns [99, 88, 43, 87]



shift()

Метод shift () похож на pop (), но действует на первый элемент массива, а не на последний.


let scores = [87, 43, 88, 99]

scores.shift() //returns 87

//scores now equals [43, 88, 99]



slice()

Возвращает подмассив на основе начальной и конечной позиции:


let scores = [87, 43, 88 99]

scores.slice(1,3)

//returns [43,88]



some()

Метод some () возвращает true, если хотя бы один элемент удовлетворяет данному условию


let scores = [87, 43, 88, 99]

scores.some((x) => {return x < 50}) //returns true
scores.some((x) => {return x < 40}) //returns false



sort()

Сортирует элементы массива:


let scores = [87, 43, 88, 99]

scores.sort() //returns [43, 87, 88, 99]
scores.sort((a,b) => {return b - a}) // returns [99, 88, 87, 43]



splice()

Метод splice () изменяет содержимое массива. Этот метод принимает три аргумента: (начальный индекс, количество удаляемых элементов, замещающие элементы)


let scores = [87, 43, 88, 99]

scores.splice(2, 0, 95) //returns []

//scores now equals [87, 43, 95, 88, 99]

let counts = [1, 2, 3, 4, 5]

counts.splice(1, 3) //returns [2,3,4]

//scores now equals [1,5]



Обратите внимание, что если мы опускаем третий аргумент, splice () просто удаляет элементы из массива. Также важно признать, что splice () возвращает удаленные элементы и изменяет существующий массив.

toString()

Просто преобразует массив в строку.


let scores = [87, 43, 95, 88, 99]

scores.toString()

//returns '87,43,95,88,99'



unshift()

Добавляет элементы в начало массива и возвращает новую длину массива.


let scores = [87, 43, 95, 88, 99]

scores.unshift(50,70) //returns 7

//scores now equals [50, 70, 87, 43, 95, 88, 99]



find()

Метод find () возвращает первый элемент, который соответствует указанному условию.


let scores = [87, 43, 95, 88, 99]

scores.find((x) => {return x > 90})

//returns 95



findIndex()

То же, что find (), но возвращает первый индекс вместо первого значения, которое удовлетворяет условию.


let scores = [87, 43, 95, 88, 99]

scores.findIndex((x) => {return x > 90})

//returns 2



entries()

Функция entries() возвращает итератор, который можно использовать для циклического перебора ключей и значений массива.


let scores = [87, 43, 95, 88, 99]

var entries = scores.entries()

console.log(entries.next().value) //returns [0, 87]
console.log(entries.next().value) //returns [1, 43]



from()

Метод from () создает новый массив из объекта, похожего на массив.


let nameArray = Array.from('Sam')

console.log(nameArray)

//logs ['S', 'a', 'm']



keys()

Метод keys () возвращает новый объект итератора, который содержит ключи для каждого индекса в массиве.


let scores = [87, 43, 95, 88, 99]
let iterator = scores.keys();

console.log(iterator.next())

//logs {value: 0, done: false}



Заключение:

Массивы - это мощные типы данных в JavaScript. Используйте массивы, если вы хотите отслеживать коллекцию значений и порядок их расположения.

Вы можете скачать готовую шпаргалку на моей странице на GitHub -> /arrays-js-es6-abc, или добавить страницу в закладки.                                                                                                                                                              

пятница, 21 декабря 2018 г.

Бэкенд на Node+Express+MongoDB

Сегодня мы создадим бэкенд-часть приложения на стэке NodeJS+ExpressJS+MongoDB



Это позволит в дальнейшем использовать наш код для развертывания приложений, в том числе и с использованием ReactJS.

Я уже рассказывал о подобных приложениях своих постах - Блог на NodeJS Express MongoDB

Главное отличие этого поста будет в использовании собственной, локально установленной базы MongoDB и я постараюсь не повторяться.

Нам потребуется установленная платформа Node.js и менеджер пакетов NPM. О том как их скачать, установить и настроить я рассказывал в своих постах ранее - Простые Советы - JAVASCRIPT.

Для того чтобы убедиться что у вас все установлено и настроено правильно выполним последовательно в КС (терминал - командная строка) следующие команды:

node -v
npm -v
mongod --version


Увидим что-то подобное.



У вас будут свои версии.

Если MongoDB не установлена, то последняя команда выдаст ошибку. В этом случае вам нужно перейти на официальный сайт MongoDB и установить нужную версию локально. Это просто и я на этом останавливаться не буду.

Для того, чтобы запустить сервер базы данных, нам нужно ввести в КС следующую команду:

mongod


Вы увидите, что то похожее:



Нам интересно последняя строчка выделенная красным. Она говорит о том. что база готова к работе и запущена на порте - 27017

Не закрывая это окно команд. Откроем еще одно - ⊞ Win + R Введем - cmd и нажмем Enter.

В этом окне введем последовательно команды:

mongo
show dbs




Получим список всех существующих баз данных.

Для того, чтобы остановить работу сервера нам нужно нажать сочетание клавиш Ctrl + C. Для остановки локального сервера (на будущее) требуется ввести еще и подтверждение - y. Затем- нажать Enter.

Перейдем к разработке серверной части приложения.

Мы будем использовать фреймворк Express

Вначале создадим папку нашего проекта и перейдем в нее:

mkdir server-side
cd server-side


Если вы используете Git для разработки, то я рекомендую сразу создать файл .gitignore, куда сразу добавить папку с модулями ноды - node_modules.

echo node_modules/ >> .gitignore




Если вы работаете в VS-code, то введите:

code .


Или иным способом откройте папку в вашем любимом редакторе кода.

В дальнейшем я буду использовать терминал VS-code, но вы можете легко использовать и КС.

Инициализация приложения

На этом этапе мы создадим папку package.json - со всеми зависимостями.

npm init


Или с флагом -y.

Об этом я очень подробно рассказывал в посте Блог на NodeJS Express MongoDB ( I ).

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



Устанавливаем Express

Для этого в КС (терминале), находясь в рабочей папке проекта:

npm install express --save


Установится фреймворк и добавится в зависимости нашего проекта - package.json



Файл index.js - точка входа.

Так как, мы когда инициализировали наш проект (создавали файл package.json), оставили по умолчанию точку входа - "main": "index.js", то нам нужно создать файл index.js в корне нашего проекта.

В нем мы сразу подключим фреймворк Express методом require. Здесь же создадим константу app, которая будет выполнять серверную часть нашего приложения.


 const express = require('express');
 const app = express();



Добавим метод get, которым будем выводить все посты на страницу по запросу на адрес:
http://localhost:3000/posts
И поставим прослушивание порта нашим сервером:


app.get('/posts', function(req, res) {
     return res.send(posts);
 });

app.listen(3000, ()=> console.log('Server running on 3000 port'));



Все вместе index.js:


 const express = require('express');
 const app = express();
 
  app.get('/posts', function(req, res) {
     return res.send(posts);
 });
 
  app.listen(3000, ()=> console.log('Server running on 3000 port'));



Следующий раздел тестовый. Я остановлюсь не нем несколько подробнее для большего понимания механизма работы фреймворка Express. Если вы с ним уже знакомы, то вы можете его пропустить и сразу перейти к следующему разделу.

Создание тестовых записей

В файл index.js, сразу после инициализации сервера ( константа app), добавим константу posts с тестовыми записями наших постов.


  const posts = [
     {
         title:  "lorem ipsum1",
         text: "Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet consectetur adipisicing elitconsectetur adipisicing elit. Pariatur fugit fuga ut voluptas nostrum ullam eum accusantium consequuntur necessitatibus dolores. Pariatur hic officiis numquam ipsam perspiciatis officia maxime iusto vitae."
     },
     {
         title:  "lorem ipsum2",
         text: "Lorem ipsum dolor sit amet  Pariatur fugit fuga ut voluptas nostrum ullam eum accusantium consequuntur necessitatibus dolores. Pariatur hic officiis numquam ipsam perspiciatis officia maxime iusto vitae."
     },
     {
         title:  "lorem ipsum3",
         text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Pariatur fugit fuga ut voluptas nostrum ullam eum accusantium consequuntur necessitatibus dolores. Pariatur hic officiis numquam ipsam perspiciatis officia maxime iusto vitae."
     },
     {
         title:  "lorem ipsum4",
         text: "Lorem ipsum dolor sitLorem ipsum dolor sit amet consectetur adipisicing elit amet consectetur adipisicing elit. Pariatur fugit fuga ut voluptas nostrum ullam eum accusantium consequuntur necessitatibus dolores. Pariatur hic officiis numquam ipsam perspiciatis officia maxime iusto vitae."
     }    
 ];



Запускаем сервер:

node index.js


Переходим по адресу: http://localhost:3000/posts



Видим все посты на странице.

У меня в браузере добавлено расширение для более красивого показа формата JSON - JSONView. У вас это может выглядеть просто строками, которые содержит переменная posts.

На этом этапе я инициализировал git и добавил все изменения в репозиторий на сайте gitHub.com - server-side-abcinblog ci -m (здесь и далее - коммит) "output all tests posts"

Теперь можно сделать вывод единственного поста на страницу по id

Для этого мы добавим метод get с двумя параметрами. Первый - путь, где через /:id мы указываем изменчивый параметр и получаем его значение из строки запроса const id = req.params.id;


app.get('/posts/:id', function(req, res) {
    const id = req.params.id;
    res.send(posts[id]);
});



Запускаем сервер, переходим по адресу http://localhost:3000/posts/1 и видим:



Если изменить в адресной строке id поста (цифра отмечена красным), то мы увидим соответствующий пост. Нумерация постов начинается по id с нуля, а мы поставили первым постом не ноль, а "пост номер один", поэтому будет выглядеть так. Мне кажется. что так понятнее и более наглядно.

Теперь разберемся с созданием новой записи. Для этого нам потребуется еще одна библиотека - body-pareser, которая поможет нам получать данные из тела запроса.

Более подробно узнать о работе с этой библиотекой можно узнать из официальной документации на gitHub и здесь.

Подключаем ее обычным способом и используем ее для написания метода post

const bodyParser = require('body-parser');
// после инициализации const app
 app.use(bodyParser.urlencoded({ extended: true}));
 app.use(bodyParser.json());
// в методах:
 app.post('/posts', function(req, res) {
    // получаем данные из тела запроса и сохраняем в конст.
    const data = req.body;
    // посмотрим что у нас там? 
    console.log(data);
    // добавляем полученные данные к постам
    posts.push(data);
    // чтобы не было бесконечного цикла - вернем все посты на страницу
    return res.send(posts);
});



После внесения изменений в файлы проекта не забывайте перезапускать сервер.
Ctrl + C и снова

node index.js


Для проверки будем использовать утилиту Postman. Я уже много раз рассказывал вам о работе с ней в моих постах. Это легко, просто и удобно. Если забыли, то посмотрите в поиске по ярлыкам.

Итак. Идем в Postman делаем GET для всех постов. Посмотрели и убедились, что все работает:



Теперь выберем запрос POSTи во вкладке body введем данные для нашего поста в формате - JSON.



Нажимаем Send и получаем ниже список со всеми постами, включая новый.



А в терминале наш console.log(data) выведет эти данные так:



Подключение к базе данных MongoDB и добавление первой тестовой записи.

Для работы с MongoDB мы будем использовать стороннюю библиотеку - mongoose, помощью который мы будем производить все манипуляции с базой данных. Для установки библиотеке в терминале (КС), в папке нашего проекта напишем команду:

npm install mongoose


Перед тем как добавить первую теcтовую запись, запустим сервер MongoDB:

mongod


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

В новом окне терминала:

mongo
show dbs


Итак, вы получите список всех созданных локально баз данных.

Удалить локальную базу данных MongoDB

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

use test1


Поле этого ввести команду:

db.dropDatabase()


Всё! База данных удалена. Для того, чтобы убедиться в этом, выведем еще раз список всех доступных баз данных:

show dbs


Все действия вы можете увидеть на скрине ниже.



Создание новой базы данных MongoDB

Здесь ве еще проще, чем можно подумать. Для создания новой базы данных я взял полностью готовый код на сайте mongoose и добавил его в наш файл index.js, предварительно я все там закомментировал.


const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/'server-side');

const Cat = mongoose.model('Cat', { name: String });

const kitty = new Cat({ name: 'Zildjian' });
kitty.save().then(() => console.log('meow'));



Красным, отмечено название создаваемой базы данных. Его можно изменить на ваше усмотрение. Если коротко разобрать то, что делает наш код, то:
  1. На первой строке - мы подключаем саму библиотеку.
  2. Ниже мы подключаемся к серверу и указываем конкретную базу данных.
  3. Третьей строкой создаем модель - таблицу, указывая к ней конкретную схему.
  4. После этого мы создаем экземпляр с названием данных.
  5. Сохраняем эти данные и после их успешного сохранения, отображаем пользователю конкретную информацию.
Если сейчас запустить сервер:

node index.js


В терминале (КС) мы увидим meow, как на фото ниже:



Давайте проверим, наличие баз данных (в другом окне):

show dbs


то мы увидим вновь созданную базу:



Установка Babel

Babel - про эту библиотеку я уже много раз рассказывал в других постах, но стоит напомнить, что она нужна для того, чтобы мы могли писать код в новом стандарте не задумываясь над его совместимостью в различных браузерах. Короче говоря, он переводит код из ES6 в ES6. Для того, чтобы установить Babel, нам нужно установить четыре библиотеки. Сам Babel и различные пресеты.

npm install babel-cli babel-core babel-preset-env babel-preset-stage-0 --save-dev


  1. babel-core
  2. babel-cli
  3. babel-preset-stage-0
  4. babel-preset-env
На этом этапе все изменения вы можете посмотреть в репо - server-side-abcinblog ci -m"reate DB - local and test"

Для проверки работы установленных пакетов, мы можем удалить, или закомментировать весь код в файле index.js и добавить код написанный на ES6 для проверки:


const arr = [1, 2, 3, 4, 5, 6];
const result = arr.map(el => el * 3);
console.log(result);



Для работы наших пакетов нам нужно создать файл конфигурации - .babelrc в корне проекта


{
    "presets": ["env","stage-0"]
}



В скрипты, файла package.json добавлю скипт, который будет запускать Babel и переводить наши файлы из файла index.js в ES5 и складывать готовые файлы в папку (которую он сам и создаст) - dist


 "scripts": {
    "build": "babel  index.js -d dist",
    "test": "echo \"Error: no test specified\" && exit 1"
  },



Теперь, для запуска нам нужно набрать команду:

npm run build


После запуска, у нас появится новая папка dist



Для конкатенации массивов таким способом - [...arr1, ...arr2], мы установим Babel-plugin: babel-plugin-transform-object-rest-spread

npm install --save-dev babel-plugin-transform-object-rest-spread


Для того, чтобы не писать несколько команд сразу в терминале, мы внесем изменения в скрипты в файле package.json


  "scripts": {
    "build": "babel  index.js -d dist",
    "start":"node dist/index.js", 
    "build-start": "npm run build && npm run start"
  },



  1. Первая команда, ранее нами добавленная, конвертирует код.
  2. Вторая команда запускает конвертированный файл из папки dist.
  3. Третья команда выполняется предыдущие две сразу, вместе.
Запусти наш код:

npm run build-start


В терминале увидим результат выполнения нашего кода:



Теперь мы можем писать код на ES6 используя все его базовые возможности, вроде импорта и деструктуризации. Если вы забыли, что это, то можно посмотреть мои поты по ES6 JavaScript.

На этом этапе все изменения вы можете посмотреть в репо - server-side-abcinblog ci -m"Babel"

Для того, чтобы наш проект имел более правильную структуру, я в корне проекта создал папку src и перенес в нее файл index.js из корня. В файле package.json я изменил сроку build в scripts следующим образом:


  "scripts": {
    "build": "babel  src -d dist",
    "start":"node dist/index.js", 
    "build-start": "npm run build && npm run start"
  },



Можно еще раз запустить:

npm run build-start


чтобы убедиться, что все работает правильно.

Создание модели

В папке src создадим папку для наших моделей - models и в ней файл - Post.js. Это модель, которая будет работать с таблицами наших статей. Нам нужно импортировать сам mongoose и Schema из библиотеки mongoose. Описываем схему для нашего поста. Нам требуется заголовок, собственно текст и дата создания записи. С помощью свойства timestamps будут добавляться эти даты.


import mongoose, { Schema } from 'mongoose';

const PostSchema = new Schema(
    {
        title: String,
        text: String,
    },
    {
        timestamps: true
    }
);
const Post = mongoose.model('Post', PostSchema);

export default Post;



В конце мы экспортируем модель Post по умолчанию. Теперь нам нужно подключиться к базе данных и создать первую запись. В файле src/index.js я все удалю и добавлю


import mongoose from 'mongoose';

import PostModel from './models/Post';

mongoose.connect('mongodb://localhost/server-side');

const post = new PostModel({
    title: "Первая запись",
    text: "Привет Мир"
});

post.save().then(() => console.log('OK'));



Здесь мне кажется. все понятно. Импортировали mongoose и саму модель PostModel. Создали экземпляр модели и указали тестовые данные. Чтобы сохранить саму запись, мы обращаемся к методу save(), который возвращает Promise.

Перед тем. как запустить код, стоит проверить, что у нас отсутствует база данных.

Так как, я уже создавал такую базу, то я выполнил ее удаление.



Запускаем код:

npm run build-start


В консоли вы увидите:



И если опять посмотреть весь список баз данных, то мы увидим вновь созданную:



Теперь выполним команды последовательно:
  1. use server-side
  2. show collections
  3. db.posts.find()




На этом этапе я сделал коммит и все изменения здесь - server-side-abcinblog ci -m"Connection with MongoDB and first tests record"

Запросы на добавление записи

В файле src/index.js подключим express и body-parser для обработки тела запроса.

Создаем приложение express

Подключаем body-parser для нашего express- приложения.

ВАЖНО - подключение к базе данных располагать сразу после создания сервера - выше остального кода.

Создаем обработчик для приема и обработки POST запросов.

Теперь вместо тестовой записи, мы будем принимать реальные данные от клиента, которые мы сохранили в переменно data

Вместо ответа в консоли мы вернет ответ в виде JSON.

Последней строкой, ставим приложение на прослушивание по определенному порту. В моем случае - 3000.


import mongoose from 'mongoose';
import express from 'express';
import bodyParser from 'body-parser';
import PostModel from './models/Post';

const app = express();

mongoose.connect('mongodb://localhost/server-side');

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.post('/posts', (req, res) => {
    const data = req.body;
    // console.log(req.body);
    const post = new PostModel({
        title: data.title,
        text: data.text
    });

    post.save().then(() => {
        res.send({ status: "OK"});
    });
});

app.listen(3000, () => console.log("Server running on 3000 port"));



Запускаем приложение.

npm run build-start


Для тестовых запросов мы продолжим использовать утилиту Postman

Делаем POST с тестовой записью и получаем статус- OK



Теперь убедимся, что запись появилась в базе данных. В консоли (mongo) сделаем еще один запрос:

db.posts.find()


И если все у вас прошло успешно то вы увидите - новую запись.



На этом этапе я сделал коммит и все изменения здесь - server-side-abcinblog ci -m"POST with Postman"

CRUD

GET - Обработчик для получения записей.

Выполнив метод Post.find() без параметров, мы получим все записи. И выполняем проверку ошибок в запросе. Это важно и нужно сделать везде.

//вверху файла к импортам добавим 
import Post from './models/Post';
// после post - запроса сразу
 app.get('/posts', (req, res) => {
     Post.find().then((err, posts) => {
         if(err){
             res.send(err);
         }
         res.json(posts);
     });
 });



Идем в постмен и делаем обычный GET - запос.



Теперь мы можем проверить и добавление записи, выведя потом все записи снова.

POST -запрос.



GET -запрос.



На этом этапе я сделал коммит и все изменения здесь - server-side-abcinblog ci -m"GET all posts"

DELETE - обработчик для удаления записей.

Здесь все просто. Мы будем использовать параметр id. Для удаления записи мы используем метод remove(), котоый принимает объект с параметрами. Проверяем, чтобы после выполнения запроса, запись была возвращена. Это будет говорить о том. что запись была успешно удалена из базы данных. Иначе - возвращаем ошибку.


 app.delete('/posts/:id', (req, res) => {
     PostModel.remove({
         _id: req.params.id
     }).then(post => {
         if(post) {
             res.json({ status: "deleted"});
         } else {
             res.json({ status: "error"});
         }
     })
 })



Теперь с помощью утилиты Postman отправим за сервер DELETE - запрос с id той записи, которую мы хотим удалить.

Сохраняем и перезапускаем сервер. Идем в Postman.

Я решил удалить вторую запись, поэтому скопировал ее id и подставил в адрес, сразу после слэша.



Нажал и получил:



Если сейчас выполнить GET всех постов, то получим:



Второго поста нет - он успешно удален.

На этом этапе я сделал коммит и все изменения здесь - server-side-abcinblog ci -m"DELETE post"

UPDATE - обработчик обновления записи.

Здесь мы будем использовать метод PUT, который принимает в параметрах id записи.

С помощью метода .findByIdAndUpdate() мы будем обновлять конкретную запись.

Первым параметром - id, вторым - объект со свойством set, которое хранит новые данные.

Не забываем про обработчик ошибок.


 app.put('/posts/:id', (req, res) => {
     PostModel.findByIdAndUpdate(req.params.id, {$set: req.body}, (err) => {
         if(err) {
             res.send(err);
         }
         res.json({ status: "update"});
     });
 });



Запускаем сервер и идем в Postman.

Здесь мы попытаемся обновить запись. Используем id.



Получили статус - status: "update"

Теперь, если мы опять выведем все посты методом GET, то мы увидим на месте второго поста - обновленные данные.



На этом этапе я сделал коммит и все изменения здесь - server-side-abcinblog ci -m"UPDATE post"

Контроллер.

Разработаем контроллер - класс, с помощью которого мы разделим логику запросов.

Выносим логику всех наших обработчиков запроса в отдельный файл, отдельной папки.

Для начала в папке src мы создадим папку - controllers и уже в ней создадим файл - PostController.js.

Этот класс контроллера будет содержать пять методов, каждый из которых будет выполнять свою задачу. Так как, слушатель принимает вторым параметром функцию, о мы просто скопируем их из файлов (index.js. Пример:


    update(req, res)  {
        PostModel.findByIdAndUpdate(req.params.id, {$set: req.body}, (err) => {
            if(err) {
                res.send(err);
            }
            res.json({ status: "update"});
        });
    }




Красным выделил то, что скопировали, но удалили стрелочную функцию. Дополнительно мы создадим метод read(), который будет возвращать конкретную статью по ее id.


import PostModel from '../models/Post';
import Post from '../models/Post';
class PostController {
    index(req, res) {
        Post.find().then((err, posts) => {
            if(err){
                res.send(err);
            }
            res.json(posts);
        });
    }
    create(req, res) {
        const data = req.body;
        // console.log(req.body);
        const post = new PostModel({
            title: data.title,
            text: data.text
        });

         post.save().then(() => {
            res.send({ status: "OK"});
        });
    }
    read(req, res) {
        PostModel.findOne({ _id: req.params.id}).then(post => {
            if(!post) {
                res.send({ error:"nor found"});
            } else {
                res.json(post);
            }
        })
    }
    update(req, res)  {
        PostModel.findByIdAndUpdate(req.params.id, {$set: req.body}, (err) => {
            if(err) {
                res.send(err);
            }
            res.json({ status: "update"});
        });
    }
    delete(req, res)  {
        PostModel.remove({
            _id: req.params.id
        }).then(post => {
            if(post) {
                res.json({ status: "deleted"});
            } else {
                res.json({ status: "error"});
            }
        });
    }
};

 export default PostController;



Теперь подключим его в основной файл:

import mongoose from 'mongoose';
import express from 'express';
import bodyParser from 'body-parser';
import PostController from './controllers/PostController';
const Post = new PostController();
// import PostModel from './models/Post'; перенесли в контроллер.
// import Post from './models/Post'; перенесли в контроллер.
const app = express();

mongoose.connect('mongodb://localhost/server-side');

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.get('/posts', Post.index);
app.get('/posts/:id', Post.read);
app.post('/posts', Post.create);
app.delete('/posts/:id', Post.delete);
app.put('/posts/:id', Post.update);

app.listen(3000, () => console.log("Server running on 3000 port"));



Функции запросов мы поменяли на методы из контроллера.

Напрямую обращаться к методам нельзя поэтому вверху файла мы создали экземпляр класса в отдельной переменной:

const Post = new PostController();

Теперь запускаем сервер и проверим все методы, через Postman, как мы это делали ранее.

Проверим создание поста, вывод всех постов, обновление и удаление.

На этом этапе я сделал коммит и все изменения здесь - server-side-abcinblog ci -m"Creating a controller"

Таким образом мы написали вся серверную часть нашего приложения.










                                                                                                                                                             

воскресенье, 28 октября 2018 г.

JavaScript трюки.

Изучите эти аккуратные трюки JavaScript менее чем за 5 минут.

Методы экономии времени, используемые профессионалами!





В этой статье мы рассмотрим:
  1. Очистка или усечение массива.
  2. Имитация именованных параметров с деструкцией объектов.
  3. Деструктуризация объектов для элементов массива.
  4. Переключение с диапазонами.
  5. await нескольких асинхронных функций с помощью async / wait
  6. Создание чистых объектов
  7. Форматирование кода JSON
  8. Удаление повторяющихся элементов из массива.
  9. Сглаживание (объединение) многомерных массивов


Все материалы по JS в этом блоге, смотрите по ссылке.


  1. Очистка или усечение массива.
  2. Легкий способ очистки или усечения массива без его переназначения - это изменить его значение свойства length:

    
    const arr = [11, 22, 33, 44, 55, 66];
    
    // truncanting
    arr.length = 3;
    console.log(arr); //=> [11, 22, 33]
    
    // clearing
    arr.length = 0;
    console.log(arr); //=> []
    console.log(arr[2]); //=> undefined
    
    


  3. Имитация именованных параметров с деструкцией объектов.
  4. Скорее всего, вы уже используете объекты конфигурации, когда вам нужно передать в переменную набор параметров для некоторой функции, например:

    
    doSomething({ foo: 'Hello', bar: 'Hey!', baz: 42 });
    function doSomething(config) {
      const foo = config.foo !== undefined ? config.foo : 'Hi';
      const bar = config.bar !== undefined ? config.bar : 'Yo!';
      const baz = config.baz !== undefined ? config.baz : 13;
      // ...
    }
    
    


    Это старый, но эффективный шаблон, который пытается имитировать именованные параметры в JavaScript. Вызов функции выглядит нормально. С другой стороны, логика обработки объектов конфигурации необоснованна. С деструктуризацией объекта ES2015 вы можете обойти этот недостаток:

    
    function doSomething({ foo = 'Hi', bar = 'Yo!', baz = 13 }) {
      // ...
    }
    
    


    И если вам нужно сделать дополнительный объект config, это тоже очень просто:

    
    function doSomething({ foo = 'Hi', bar = 'Yo!', baz = 13 } = {}) {
      // ...
    }
    
    


  5. Деструктуризация объектов для элементов массива.
  6. Назначение элементов массива отдельным переменным с деструкцией объектов:

    
    const csvFileLine = '1997,John Doe,US,john@doe.com,New York';
    
    const { 2: country, 4: state } = csvFileLine.split(',');
    
    


  7. Переключение с диапазонами.
  8. ПРИМЕЧАНИЕ. После некоторого раздумья я решил выделить этот трюк от других в этой статье. Это НЕ экономия времени, НO он подходит для кода реальной жизни. Имейте в виду: IF в таких ситуациях всегда лучше. В отличие от других советов в этом посте, это скорее любопытство, чем что-то для реального использования. Во всяком случае, я сохраню его в этой статье по историческим причинам.

    Вот простой способ использовать диапазоны в операторах switch:

    
    function getWaterState(tempInCelsius) {
      let state;
      
      switch (true) {
        case (tempInCelsius <= 0): 
          state = 'Solid';
          break;
        case (tempInCelsius > 0 && tempInCelsius < 100): 
          state = 'Liquid';
          break;
        default: 
          state = 'Gas';
      }
      return state;
    }
    
    


  9. await нескольких асинхронных функций с помощью async / wait.
  10. Можно ожидать - await завершения нескольких асинхронных функций с помощью Promise.all:

    
    await Promise.all([anAsyncCall(), thisIsAlsoAsync(), oneMore()])
    
    


  11. Создание чистых объектов.
  12. Вы можете создать 100% чистый объект, который не наследует какое-либо свойство или метод из Object (например, constructor, toString () и т. д.).

    
    const pureObject = Object.create(null);
    
    console.log(pureObject); //=> {}
    console.log(pureObject.constructor); //=> undefined
    console.log(pureObject.toString); //=> undefined
    console.log(pureObject.hasOwnProperty); //=> undefined
    
    


  13. Форматирование кода JSON.
  14. JSON.stringify может сделать больше, чем просто подкрепить объект. Вы также можете украсить свой JSON-вывод:



    
    const obj = { 
      foo: { bar: [11, 22, 33, 44], baz: { bing: true, boom: 'Hello' } } 
    };
    
    

    
    // Третий параметр - это количество пробелов, используемых для
    // украшения вывода JSON.
    // JSON.stringify (obj, null, 4); 
    // =>"{
    // =>    "foo": {
    // =>        "bar": [
    // =>            11,
    // =>            22,
    // =>            33,
    // =>            44
    // =>        ],
    // =>        "baz": {
    // =>            "bing": true,
    // =>            "boom": "Hello"
    // =>        }
    // =>    }
    // =>}"
    
    


  15. Удаление повторяющихся элементов из массива.
  16. Используя ES2015 Sets вместе с оператором Spread - (...), вы можете легко удалить повторяющиеся элементы из массива:

    
    const removeDuplicateItems = arr => [...new Set(arr)];
    
    removeDuplicateItems([42, 'foo', 42, 'foo', true, true]);
    //=> [42, "foo", true]
    
    


  17. Сглаживание (объединение) многомерных массивов
  18. Сглаживание массивов тривиально с оператором Spread:

    
    const arr = [11, [22, 33], [44, 55], 66];
    
    const flatArr = [].concat(...arr); //=> [11, 22, 33, 44, 55, 66]
    
    


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

    
    function flattenArray(arr) {
      const flattened = [].concat(...arr);
      return flattened.some(item => Array.isArray(item)) ? 
        flattenArray(flattened) : flattened;
    }
    
    const arr = [11, [22, 33], [44, [55, 66, [77, [88]], 99]]];
    
    const flatArr = flattenArray(arr); 
    //=> [11, 22, 33, 44, 55, 66, 77, 88, 99]
    
    


    И вот оно! Надеюсь, эти аккуратные маленькие трюки помогут вам писать лучше и красивее на JavaScript.




Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.


Удачного кодирования!                                                                                                                                                              

Телеграм канал - Full Stack JavaScript Developer
Помочь проекту (любая валюта). DONATE


Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com