Translate

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

среда, 26 декабря 2018 г.

Redux - простое руководство.

Пришло время разобраться с Redux и написать простое приложение с его использованием.




Посмотреть готовое приложение можно на хосте.

Я не буду долго рассказывать зачем он нужен и как он работает. В сети есть масса информации.

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

Для простоты доступа к данным и работы с ними, мы будем использовать Redux.



Стоит напомнить, что Redux использует Flux архитектуру (не путайте с Flux-библиотекой, которая тоже использует такую архитектуру). Flux предполагает однонаправленный поток данных - One-way binding

Для простоты понимания этого, стоит посмотреть на картинку <<Схема>>:



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

Store - это хранилище и именно так его нужно понимать в данном контексте. По своей сути Store - это обыкновенный объект, который и определяет, как у нас будет выглядеть наши компоненты.

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

Далее, у нас идёт Dispatcher. Dispatcher это центральный узел, который обрабатывает все наши потоки данных. То есть - всё проходит через него! А конкретно, через него проходит те данные, которые определяют как раз-таки action.

Значит есть один action (слева на картинке). Этот Action у нас стоит самого начала и считаете это теми данными которые идут по умолчанию. То есть, по умолчанию поступает какие-то данные в Action говорит диспетчеру - "Покажи мне Username такой-то и Lastname такой-то!". И диспетчер заносит изменения в Store, откуда они подтягиваются View (React -components) и мы видим картинку на экране.

В тоже время, если мы хотим поменять нашу визуальную картинку со стороны пользователя, мы так же создаем новый Action. Новый Action, который опять обращается к диспетчеру и делает тоже самое. Говорит диспетчеру "Обнови username Коля на username Вася" и он его обновляет. Это определяет уже наш View.

Вот в этом собственное заключается вся суть архитектуры Flux и самого принципа One Way binding, то есть однонаправленного потока данных. Если вы заметили, то у нас поток данных идёт всегда в одну сторону!

View не может непосредственно повлиять на что-то, что есть в нашим хранилище! Это главное отличие его от так называемого Two-way binding, например, который использует ангуляр.

Действия загрузки данных.

Передача данных.

Для простоты, мы будем использовать готовое решение create-react-app. Как его установить я рассказывал в своих постах о React Переходим в папку нашего приложения и устанавливаем два дополнительных модуля:
  1. redux
  2. react-redux
Эти модули позволят нам использовать весь функционал Redux и просто совместить его с React.

npm install redux react-redux


--save-dev команду можно не писать.

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

Выбираем файл index.js в папке src - это точка входа в наше приложение.

Импортируем компонент Provider из модуля react-redux

import { Provider } from "react-redux";

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


ReactDOM.render(<Provider>
                    <App />
                </Provider>, document.getElementById('root'));


Следующее, что нам нужно сделать, это импортировать функцию, которая и создает Store.

import { createStore } from 'redux';

Для создания хранилища - Store напишем:

const store = createStore(()=>{});

В качестве параметра эта функция должна принимать reducer. Сам по себе редусер - это просто функция, поэтому мы для начала, можем записать вместо него , пустую функцию, например стрелочную - ()=>{}.

Для соединения провайдера с хранилищем Store, мы добавим Provider атрибут store, со значением store.


ReactDOM.render(<Provider store={store}>
                    <App />
                </Provider>, document.getElementById('root'));


Таким образом, мы создали Store, обернули его в провайдер и сделали данные доступные для наших компонентов.

Создание Reducers.

В папке src создадим папку reducers и в ней два файла index.js и info.js.

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

Сам термин пришел к нам из JS Array.prototype.reduce().

Вся суть редусера - это взять предыдущее, значение, плюс текущее значение и в конце - вернуть новое значение.

То есть, мы берем предыдущее состояние State, то что нам нужно поменять, соединяем это и получаем что-то новое, что в последствии у нас отрендерится на экране.

Создадим первый редусер в файле info.js


// имитация базы данных
const initialState = {
    user: "unknown user"
}
//  сам редусер 
export default function userInfo (state = initialState) {
    return state
}


Функция принимает в качестве стандартного значения - initialState и возвращает новый state на основании старого.

Проще говоря редусер принимает один state, а возвращает новый state.

Файл index.js в папке reducers, нам потребуется для того, чтобы все редусеры держать в одном месте, но в разных файлах. В качестве "родительского" редусера мы будем использовать index.js. В этом нам поможет, то что уже реализовано в библиотеке Redux - combineReducers


import { combineReducers } from "redux"
import userInfo from "./info"

const rootReducer = combineReducers({
  userInfo
})

export default rootReducer;



Теперь, самое время вернуться в файл index.js в корне проекта, и заменить анонимную, пустую функцию на rootReducer. И добавим импорт редусеров в наш файл.


import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

import { Provider } from "react-redux";
import { createStore } from "redux";
import rootReducer from "./reducers/index";

const store = createStore(rootReducer);

ReactDOM.render(<Provider store={store}>
                    <App />
                </Provider>, document.getElementById('root'));
serviceWorker.unregister();



Таким образом наш Store, будет знать, что когда мы к нему обращаемся, то он будет вызывать все редусеры, которые у нас есть.

Store по своей сути, это просто обычный объект, который содержит State для всего нашего приложения. То есть всё, что у нас может изменяться, обо всём этом должен знать State и он это регулирует.

Фактически, мы сейчас с помощью нашей функции в редусере ( function userInfo ), занесли в наш State данных переменной user со значением "unknown user". Теперь осталась нам подключить наш компонент к этому Store и взять с него то что принадлежит нам по праву, а именно наши свойства.

App.js


import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

import { connect } from "react-redux"

class App extends Component {
  render() {
    return (
          <h2>Welcome to React</h2>
    );
  }
}


export default connect()(App);



Чтобы подключить Redux к компоненту есть специальная функция - connect

Подключаем ее:

import { connect } from "react-redux";

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

export default connect()(App);

Если сейчас запустит и посмотреть приложение:



Здесь можно видеть, что функция connect создает дополнительный компонент над компонентом App.

Передача свойств приложению.

Для передачи свойств приложению:

App.js

import React, { Component } from 'react';

import './App.css';

import { connect } from "react-redux"

class App extends Component {
  render() {
    return (
          <h2>Welcome to React {this.props.user}</h2>
    );
  }
}

function mapStateToProps(state) {
  return {
    user: state.userInfo.user
  }
}

export default connect(mapStateToProps)(App);



Создаем дополнительную функцию mapStateToProps. Название произвольное, но оно рекомендуется, чтобы другие разработчики понимали то, что делает эта функция. Эта функция будет передавать наши state в свойства. Функция принимает наш state, а возвращать будет некую переменную ( для логичности - user), а в качестве значения возьмем те данные, которые указали на данный момент в редюсере.

Чтобы обратится к этим данным, нам нужно обратиться к нашему state, с которого мы подтягиваем данные. Дaлее к userinfo из rootReducer и уже используя userinfo мы попадаем в наш изначальный редусер и получаем доступ к значению переменной user в файле info.js.

user: state.userInfo.user

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



Таким образом мы с помощью функции передали в компонент App свойство user со значением unknown user

Мы смогли передать те значения, которые мы изначально несли в нашу "Базу данных" - const initialState.

Вывести свойства на экран нам легко - {this.props.user}

Практически, мы получили тоже самое, как если бы передали свойство user={"unknown user"} непосредственно в компонент реакт.

Псомотреть весь код приложения, можно в репо - redux-simple-abc ci -m"Simple data transfer with Redux"

Действия пользователя.

Действия - Actions

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

Действия пользователя будут представлены в Action, который на картинке <<Схема>> вверху.

Важно запомнить, что Action определяют что именно нужно поменять, а Reducer - как именно.


Для лучшего понимания работы редусеров, следует вспомнить, как различаются компоненты в React:



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

В папке src создадим папки: containers и components.

В папке components создадим: User.js и Year.js

В файле User.js создадим простой, глупый компонент - Dumb Component:


import React from "react"
export default class User extends React.Component {
  render() {
    return (
      <h2>Welcome to React {this.props.user}</h2>
    );
  }
}



Его render() мы взяли из файла App.js.

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


import React from "react"

export default class Year extends React.Component {
  constructor(props) {
    super(props)
    this.onBtnClick = this.onBtnClick.bind(this)
  }

  onBtnClick(event) {
    return this.props.setYear(event.target.textContent)
  }

  render() {
    return <div>
      <button onClick={this.onBtnClick}>1975</button>
      <button onClick={this.onBtnClick}>1991</button>
      <button onClick={this.onBtnClick}>2015</button>
      <p>This year has been chosen - {this.props.year}</p>
    </div>
  }
}



Если коротко, то создали компонент, который будет выводит кнопки. На кнопки навесили событие onClick, которое вызывает функцию onBtnClick. В конструкторе прибиндили эту функцию. Эта функция возвращает функцию setYear(), которая в качестве аргумента принимает значение кнопки (текст кнопки - в данном случае год) - event.target.textContent.

Таким образом мы написали два dumb-компонента.

Теперь нам нужно поменять файл App.js

Поскольку он у нас будет выполнять функцию smart - компонента, мы переместим его в папку src/containers

Так как мы поменяли директорию файла App.js, то в файле нам нужно изменить путь подключения его в файле index.js в корне сайта.

import App from './containers/App';

На этом этапе нам нужно запустить приложение, чтобы убедиться, что оно по прежнему работает.



Посмотреть весь код приложения на этом этапе, можно в репо - redux-simple-abc ci -m"divided the application into components"

Теперь нам необходимо в наш smart - компонент App.js добавить наши dump-компоненты - User.js и Year.js


    import React, { Component } from 'react';
    import './App.css';
    import User from '../components/User';
    import Year from '../components/Year';
    import { connect } from "react-redux"

    class App extends Component {
      render() {
        return (
           <div>
            <User />
            <Year />
          </div>
              
        );
      }
    }

    function mapStateToProps(state) {
      return {
        user: state.userInfo.user
      }
    }

    export default connect(mapStateToProps)(App);


Если сейчас посмотреть в браузер, то мы увидим:



Вывели на экран все статические данные, кроме необходимых переменных.

Переменная user не задействована в экшенах и мы ее можем легко отобразить прямо сейчас:

<User user={this.props.user} />



Эту переменную мы не меняем в нашем приложении и вывод ее на экран это просто свидетельство того, что Redux работает.

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

В папке src создаем папку actions а в ней файл actionYear.js


export default function setYearAction(year) {
    return {
      type: "SET_YEAR",
      payload: year
    }
  }



Экшен по своей сути это обычный объект. Для удобства их создают через специальные функции, которые называются Action Creators. Это упрощает чтение кода и очень удобно для разработки!

Название функции setYearAction - произвольное, но лучше, если оно будет понятным.

Функция обязательно возвращает объект и в нем должно быть обязательно поле - type.

Поле - type, чем то похоже на идентификатора экшена, и значение этого поля всегда строка с заглавными буквами - UPPERCASE.

Вторая переменная payload, будет принимать значение year то, которое мы передаем в эту функцию изначально.

payload - тоже вне гласное соглашение. Обозначает что-то типа "полезной нагрузки".

Более подробно о формате написания экшенов можно почитать в стандартах - Flux Standard Action

Как мы говорили ранее - экшен - ЧТО МЕНЯЕМ... А мы меняем -year, который в аргументе и меняем его экшеном - "SET_YEAR"

Теперь нам нужно понять КАК нам его изменить.

За это у нас отвечает редусер.

Переходим в редусеры src/reducers/info.js и будем его усовершенствовать.

Добавим некий изначальный стейт - например: year: 2015, а в самом редусере добавим еще один аргумент - action

Таким образом редусер будет принимать начальное состояние: state = initialState и то, что должно измениться - action, все это соединяет и возвращает нам новый стейт на основании проделанной работы.

Обычно их указывают в формате switch/case.

В нем обратимся к типу экшена и обработаем различные варианты.

Если у нас тип = "SET_YEAR", то мы возвращаем весь предыдущий стейт ( ...state) и ключ year с новым значением action.payload.

Другими совами: Мы берем наш изначальный стейт :

reducers/info.js

const initialState = {
    user: "unknown user",
    year: 2015
}



Вторым аргументом берем стейт из экшена: actions/actionYear.js
export default function setYearAction(year) {
    return {
      type: "SET_YEAR",
      payload: year
    }
  }



Соединяем их вместе и получаем что-то новое! А конкретно - изменяем поле year на то, которое указано в экшене.

Теперь нашей задачей является - доделать наш "умный компонент", который должен отправлять данные в Store.

Переходим в src/containers/App.js

Первое - импортировать функцию из actionYear.js

import setYearAction from '../actions/actionYear';

Выведем год статически:


    import React, { Component } from 'react';
import './App.css';
import User from '../components/User';
import Year from '../components/Year';
import setYearAction from '../actions/actionYear';
import { connect } from "react-redux";

class App extends Component {
  render() {
    return (
      <div>
        <User user={this.props.user} />
        <Year year={this.props.year}/>
      </div>
          
    );
  }
}

function mapStateToProps(state) {
  return {
    user: state.userInfo.user,
    year: state.userInfo.year
  }
}

export default connect(mapStateToProps)(App);


Посмотрим в браузере:



Таким образом мы вывели "стартовые значения" стейта.

Теперь нам нужно оживить наши кнопки!

Для этого нам нужно передать новые данные диспетчеру, а он их добавит в Store.

Для этого есть специальная функция - mapDispatchToProps(dispatch) .Она принимает аргумент - dispatch и возвращает некий объект.


function mapDispatchToProps(dispatch) {
  return {
    setYearFunction: year => {
      dispatch(setYearAction(year))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App);



setYearFunction - произвольное название. Мы выбрали его, чтобы просто отличать функцию от других. В качестве ключа в нее мы передаем функцию, которую мы записали в стрелочном формате. Эта функция принимает аргумент year, который передаст наш экшен actionYear и с ним мы в функции dispatch() вызываем экшен креатор. - setYearAction(year) и в качестве аргумента ей передадим тот же year

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

Если посмотреть в браузер, то в панели разработчика мы увидим:



Поскольку мы подключили в наш компоненту новую функцию - mapDispatchToProps она передала в наш компоненте новое свойство - setYearFunction (как мы его и назвали в этой функции)

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

В файле App.js передадим компоненту Year напишем свойство, которому передадим функцию setYearFunction из ретурна mapDispatchToProps

<Year year={this.props.year} setYear={this.props.setYearFunction} />

this.props - пишем, потому что функция передается в качестве свойства.

Теперь становится ясным, что в функции onBtnClick мы как раз и возвращаем это свойство со значением текста кнопки:

Файл: components/Year.js

 onBtnClick(event) {
    return this.props.setYear(event.target.textContent)
  }



Теперь самое время посмтреть наше приложение и проверить, что все работает правильно:



Коротко:

Все начианется в нашем компоненте - Year.Здесь есть кнопки, при нажатии на которые срабатывает функция onBtnClic

Эта функция возвращает нам event.target.textContent - то есть в данном случае это текст кнопки.

Далее эти данные попадают в функцию mapDispatchToProps(dispatch) и в ней уже year будет равен тому, что мы указали в компоненте и функция dispatch(setYearAction(year)) отправляет наш экшен в хранилище -Store с помощью метода connect(mapStateToProps, mapDispatchToProps)(App)

В Store запускается редусер.Он принимает начальный стейт и экшен, который ему прислал mapDispatchToProps(dispatch), срабатывает условие - "SET_YEAR" и year меняет значение на новое - action.payload.

Далее возвращается в приложение функцией mapStateToProps(state) и мы получаем доступ к этим данным.

Посмотреть весь код приложения, можно в репо - redux-simple-abc ci -m"Finish"





                                                                                                                                                             

воскресенье, 23 декабря 2018 г.

React-Router простое руководство.

Всем привет И сегодня я вам хочу рассказать что такое реакт-роутер.




Работу простого реакт-роутера вы можете увидеть здесь

Итак, давайте начнём сначала и определим, что вообще такое роутер?

Это такой небольшой программный модуль который помогает нам распоряжаться нашими Url - адресами в браузере. Если всё что мы делали ранее было размещено на одной странице и поэтому в роутере смысла никакого не было, то теперь мы можем с помощью роутера разбивать информацию на нашей странице на некоторые такие логические куски. То есть на заглавной странице у нас будет одна информация, на странице контактов - другая. На странице "О нас" - третья и т.д.

Если говорить конкретно о реализации роутера в react приложении, то есть, как минимум три способа как это можно сделать:
  1. - это написать свой собственный роутер.
  2. - использовать стороннюю библиотеку в которой уже реализован роутер, например Ember.js или другие какие-нибудь.
  3. - использовать специальный npm package который называется реакт-роутер.
Поскольку первый вариант нужно очень долго зарабатывать чтобы он был похож на хороший роутер 2-ой вариант немного выбивается из общей картины нашего react приложения, мы будем использовать 3-ий вариант, а именно - npm module react-router.

Само реакт-приложение мы развернем с помощью create-react-app.

В общем поехали!
И так первое что нужно сделать это установить create-react-app, то есть - нашу заготовку для react приложения.

Установка самого React-приложения.

Перейдем на страницу этого модуля facebook/create-react-app и здесь вы можете подчеркнуть всю необходимую для себя информацию.

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

Вы можете делать так, как вам более удобно. Я использую новый вариант установки. Он будет зависеть от версии вашего npm. Чтобы узнать ее, вам нужно открыть терминал или КС(командная строка, окно команд) в Windows. ⊞ Win + R, написать - cmd и нажать Enter.

npm -v


Если вы предпочитаете развернуть проект из глобально установленного, то вы можете просто выполнить команду:

create-react-app name_your_project_folder


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

cd name_your_project_folder


И теперь, чтобы запустить приложение нужно набрать:

npm start




А перейдя по адресу: http://localhost:3000/ вы увидите работающее приложение.

Действительно это очень быстро и очень удобно но тут есть один маленький минус. Данное приложение включает в себя массу различных модулей которые возможно вы даже не знаете зачем они нужны и не будете их использовать. Чтобы посмотреть состав этого приложения - можно ознакомиться на сайте этого модуля. Здесь всё достаточно нормально написано по поводу того, что он использует.

Теперь на очереди реакт-роутер.


Устанавливаем react-router

Посмотреть документацию можно по ссылке: ReactTraining/react-router

Поскольку реакт-роутер не входит в стандартный набор, то мы должны установить его вручную.

Для того чтобы это сделать, нужно остановить сервер и перейти в командную строку Ctrl + C -> y и нажать Enter.

Здесь пишем следующее:

npm install react-router --save


и ставим флажок --save чтобы сохранить его package.json зависимости.

Также нам понадобится ещё один дополнительный модуль это react-router-dom, который позволяет нам более легко оперировать с процессом роутинга.

npm install react-router-dom --save


Можно сделать и одной командой:

npm install react-router react-router-dom --save


Теперь можем переходить непосредственно к созданию самого роутера.

Пишем роутер.

Итак, я захожу в папку со своей приложением и чтобы редактировать приложение созданное с помощью create-react-app, нам необходимо редактировать файл App.js и index.js в папке src.

Если мы вернемся на нас нашу страничку, где можно скачать модуль react-router, то здесь вы увидите что они нам демонстрируют два варианта использования данного плагина 1-ый с использованием ES6 и второй это CommonJS. То есть, по сути разница между ними никакой нет.

Если рассмотреть более подробно файл App.js, то вверху мы импортируем сначала react и компонент чтобы не писать React.Component каждый раз при создание нового компонента.

Далее, импортируют Logo для того чтобы создать нашу картинку. Здесь ничего нестандартного. Все это я рассказывал в своих постах о REACT ранее.

Поскольку нам это приложение уже не нужно, то мы удаляем все из файла App.js.

Итак начнем. Чистый файл App.js.
Я сначала импортирую все необходимые нам модули.

import React, { Component } from 'react';

Далее импортируем несколько новых компонентов из библиотеки react-router. Если вы хотите импортировать именно компоненты вам нужно их указывать в фигурных скобках, как Component, выше. Если же нет, то можно писать просто как импорт React.

Импортируем необходимые компоненты из react-router-dom

import { BrowserRouter, Route, Link } from 'react-router-dom';

И так что делают компоненты?

BrowserRouter - тот самый роутер. Он использует HTML5 History API и следит за тем чтобы ваш UI был всегда синхронизирован с тем что написано в адресной строке.

Следующий Route - он отвечает за отображение непосредственного UI И следит за тем чтобы пути совпадали между компонентом и адресной строкой.

и Link - это такая немножко видоизмененная ссылка под react для редиректа на нужную нам страницу.

Также я рекомендую сделать еще одну небольшую вещь:

import createBrowserHistory from 'history/createBrowserHistory';


UPDATE:В последней версии реакт-роутера, рекомендуют импортировать и подключать так:
import { createBrowserHistory } from "history";
const history = createBrowserHistory();




Это нужно для того, чтобы импортировать createBrowserHistory. Дополнительный модуль history устанавливается вместе с установкой react-router. Он нужен он для того чтобы наше джаваскрипт приложение вело свою историю одинаково, независимо от той среды в которой оно находится.

И давайте сразу его заключим некоторую константу.

const history = createBrowserHistory();

Скобки - поскольку эта функция.

Далее, нам необходимо создать примеры наших страниц, которые мы будем использовать.

Я создам самую самую элементарную базовую структуру нашего сайта. При желании Вы можете усложнить и это будет не так сложно сделать.

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

Первая страница которой она у нас будет это страница Home

const Home и используем стрелочные функции, но попрошу заметить, что мы здесь использовали второй раз круглые скобки из-за того чтобы мы не использовали фигурные скобки. Разница между фигурными и обычными скобками заключается в том, что обычные скобки возвращают всего лишь одну строку кода, а фигурные скобки много строк кода.

Но поскольку у нас JSX элемент является по факту одним элементом и занимает одну строку, мы можем использовать смело круглые скобки.

И это будет абсолютно нормально.

Продублирую этот компонент несколько раз и переименуем и вот что получилось:


const Home = () => (
  <div>
    <h2>Home</h2>
  </div>
);

const About = () => (
  <div>
    h<2>About</h2>
  </div>
);

const Contacts = () => (
  <div>
    <h2>Contacts</h2>
  </div>
);



Теперь начинается самое интересное!

Нам необходимо создать наш роутер. Роутер - Это такой же компонент как и все остальные. Приведу код файла App.js полностью, а ниже - пояснения. Создаем его:


import React, { Component } from 'react';

import { BrowserRouter, Route, Link } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';

const history = createBrowserHistory();

const Home = () => (
  <div>
    <h2>Home</h2>
  </div>
);

const About = () => (
  <div>
    <h2>About</h2>
  </div>
);

const Contacts = () => (
  <div>
    <h2>Contacts</h2>
  </div>
);

class Menu extends Component {
  render() {
    return (
      <BrowserRouter history={history}>
        <div>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/contacts">Contacts</Link></li>
          </ul>
          <hr />
          <Route exact path="/" component={Home} />
          <Route exact path="/about" component={About} />
          <Route path="/contacts" component={Contacts} />
        </div>
      </BrowserRouter>
    );
  }
}

export default Menu;



BrowserRouter это есть непосредственно роутер. К нему мы напишем сразу же атрибут history, то есть мы используем константу history для того, чтобы использовать history/createBrowserHistory библиотеку.

Дальше у нас идёт обычный div и в нём уже будут заключительные наши ссылки и пути к ним.

Их поместим внутри списка ul.

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

Каждый Link имеет специальный атрибут to и он и отвечает за то, на какую страницу у нас будет идти редирект.

1 -ый у нас будет идти на страницу Home. Обычно её отображает как слэш - "/".

2 -ой нас будет вести на страницу About, и так далее.

Дальше, для визуального разделения навигации и контента страницы, напишем стандартный тег <hr />

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

Данный компонент принимает параметр path. Тут мы указываем такой же путь, как мы указали в линках и указан параметр component, который отвечает за рендер именно того компонента который нам нужен.

В данном случае, нам нужен компонент Home По такому же принципу заполняем и другие роуты.

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

Чтобы это сделать напишем внизу файла:

export default Menu;

Теперь нужно внести некоторые изменения в файл index.js

import Menu from './App';

ReactDOM.render(<Menu />, document.getElementById('root'));

Мы импортировали Menu, вместо App, и рендерить на страницу - отправляем Menu, а не App.

Можно смотреть браузер.



Вот мы видим наши простенькое структура. У нас есть наши "кнопки-стрелялки" которые мы указали, и рендер нашей странице, за которую отвечает каждый Link.

То есть, если мы находимся по адресу localhost: 3000 - это является адресом страницы Home. Если же мы нажмем на кнопку Аbout, то рендерится компонент Аbout, но как вы уже заметили почему-то наша компонент Home не пропал!



Это происходит потому, что в нашем коде необходимо указать дополнительные параметр - exact.

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

Если мы сейчас придём на нашу страницу, то компонент Home рендерится только тогда, когда мы находимся на нашей главной странице.

Чтобы это наглядно продемонстрировать, я могу дописать exact, например к ссылке About.

Теперь, если я захожу на вкладку About, в адресной строке написано - about, и если я через слэш допишу ещё какую-нибудь абракадабру конечно же такой ссылки нету, то наш компоненты About уже не высвечивается.

Можете сами в этом убедиться.

Но если же я уберу здесь exact то чтобы мы здесь не написали компонент About будет рендерится всё равно.

Об этом следует помнить!

А вообще, обо всех react-router методах и компонентах, вы можете почитать в официальной документации - reacttraining.com.

Всё достаточно адекватно написано и особенно радует что здесь есть примеры, которые можно использовать в реальных проектах. Это очень удобно. Просто копируете их в ваш проект и всё.

Файлы проекта, на этом этапе, вы можете увидеть по адресу: router-react-abc ci -m"React-Router simple" (ci -m - коммит).

Для простоты дальнейшего использования, масштабирования, я решил разделить компоненты. Для этого я создал папку components в папке src и каждый компонент поместил в отдельный файл с соответствующим названием.

Эти компоненты я подключил в файл App.js. Для примера, добавил еще один файл компонента - Blog.js

Файлы проекта, на этом этапе, вы можете увидеть по адресу: router-react-abc ci -m"Split application into components." (ci -m - коммит).

Это всё что я хотел вам сегодня рассказать. До встречи!



                                                                                                                                                             

пятница, 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"

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










                                                                                                                                                             


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