Разработка сайтов в Константиновке, ДНР. React Hooks: как начать и создать свой собственный

 
 

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

Что такое React-хуки?

React Hooks — это специальные функции, которые позволяют вам «подключаться» к функциям React в функциональных компонентах. Например, useStateхук позволяет добавлять состояние, а useEffectтакже выполнять побочные эффекты. Раньше побочные эффекты реализовывались с помощью методов жизненного цикла. С хуками в этом больше нет необходимости.

Это означает, что вам больше не нужно определять класс при создании компонента React. Оказывается, архитектура классов, используемая в React, является причиной множества проблем, с которыми разработчики React сталкиваются каждый день. Мы часто пишем большие, сложные компоненты, которые трудно разбить. Связанный код распределен по нескольким методам жизненного цикла, что становится сложным для чтения, обслуживания и тестирования. Кроме того, нам приходится иметь дело с thisключевым словом при доступе к stateметодам propsи. Мы также должны связать методы, thisчтобы обеспечить их доступность внутри компонента. Затем у нас есть проблема чрезмерного сверления пропеллеров — также известная как ад обертки — при работе с компонентами более высокого порядка.

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

О React Hooks впервые было объявлено на конференции React, состоявшейся в октябре 2018 года, и они официально стали доступны в React 16.8. Функция все еще находится в стадии разработки; в хуки по-прежнему переносится ряд функций класса React. Хорошей новостью является то, что вы можете начать использовать их прямо сейчас. Вы по-прежнему можете использовать компоненты класса React, если хотите, но я сомневаюсь, что вы захотите этого после прочтения этого вводного руководства.

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

Предпосылки

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

Если вы хотите следовать примерам, у вас уже должно быть настроено приложение React. Проще всего это сделать с помощью инструмента Create React App. Чтобы использовать это, у вас должны быть установлены Node и npm. Если нет, перейдите на страницу загрузки Node.js и скачайте последнюю версию для своей системы (npm поставляется вместе с Node). Кроме того, вы можете ознакомиться с нашим руководством по установке Node с помощью диспетчера версий.

Установив Node, вы можете создать новое приложение React следующим образом:

npx create-react-app myapp

Это создаст myappпапку. Перейдите в эту папку и запустите сервер разработки следующим образом:

cd myapp

npm start

Откроется браузер по умолчанию, и вы увидите свое новое приложение React. Для целей этого руководства вы можете работать с Appкомпонентом, расположенным по адресу src/App.js.

Вы также можете найти код для этого туториала на GitHub, а также демо-версию готового кода в конце этого туториала.

useStateКрюк _

Теперь давайте посмотрим на некоторый код. Хук useState, вероятно, самый распространенный хук, поставляемый с React. Как следует из названия, он позволяет вам использовать stateфункциональный компонент.

Рассмотрим следующий компонент класса React:

import React from «react»;

export default class ClassDemo extends React.Component {

constructor (props) {

super (props) ;

this.state = {

name: «Agata»

};

this.handleNameChange = this.handleNameChange.bind (this) ;

}

handleNameChange (e) {

this.setState ({

name: e.target.value

}) ;

}

render () {

return (

 

 

 

 

 

 

<input

type="text"

name="name"

id="name"

value={this.state.name}

onChange={this.handleNameChange}

/>

 

 

 

 

Hello {this.state.name}

 

 

) ;

}

}

Если вы следуете вместе с Create React App, просто замените содержимое App.jsуказанным выше.

Вот как это выглядит:

Имя класса React Hooks

Дайте себе минуту, чтобы понять код. В конструкторе мы объявляем nameсвойство нашего stateобъекта, а также привязываем handleNameChangeфункцию к экземпляру компонента. Затем у нас есть форма с полем ввода, значение которого равно this.state.name. Содержащееся значение this.state.nameтакже выводится на страницу в виде приветствия.

Когда пользователь вводит что-либо в поле ввода, handleNameChangeвызывается функция, которая обновляет stateи, следовательно, приветствие.

Теперь мы напишем новую версию этого кода, используя useStateхук. Его синтаксис выглядит следующим образом:

const [state, setState] = useState (initialState) ;

Когда вы вызываете useStateфункцию, она возвращает два элемента:

state: название вашего состояния — например, this.state.nameили this.state.location.

setState: функция для установки нового значения для вашего состояния. Похоже на this.setState ({name: newValue}).

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

import React, { useState } from «react»;

export default function HookDemo (props) {

const [name, setName] = useState («Agata») ;

function handleNameChange (e) {

setName (e.target.value) ;

}

return (

 

 

 

 

 

 

<input

type="text"

name="name"

id="name"

value={name}

onChange={handleNameChange}

/>

 

 

 

 

Hello {name}

 

 

) ;

}

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

Весь конструктор класса был заменен useStateхуком, который состоит только из одной строки.

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

Код JSX стал чище, так как вы можете ссылаться на локальные значения состояния без использования this.state.

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

Примечание: при использовании React Hooks обязательно объявляйте их в верхней части вашего компонента, а не внутри условного оператора.

Несколько useStateкрючков

Но что, если мы хотим объявить более одного свойства в состоянии? Без проблем. Просто используйте несколько вызовов useState.

Вот пример компонента с несколькими useStateхуками:

import React, { useState } from «react»;

export default function HookDemo (props) {

const [name, setName] = useState («Agata») ;

const [location, setLocation] = useState («Nairobi») ;

function handleNameChange (e) {

setName (e.target.value) ;

}

function handleLocationChange (e) {

setLocation (e.target.value) ;

}

return (

 

 

 

 

 

 

<input

type="text"

name="name"

id="name"

value={name}

onChange={handleNameChange}

/>

 

 

 

 

<input

type="text"

name="location"

id="location"

value={location}

onChange={handleLocationChange}

/>

 

 

 

 

Hello {name} from {location}

 

 

) ;

}

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

Теперь давайте перейдем к следующему базовому React Hook.

useEffectКрюк

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

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

Вот простой пример:

componentDidMount () {

document.title = this.state.name + «from «+ this.state.location;

}

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

componentDidUpdate () {

document.title = this.state.name + «from «+ this.state.location;

}

При обновлении формы теперь должно обновляться и название документа.

Давайте посмотрим, как мы можем реализовать ту же логику, используя useEffectхук. Обновите компонент функции выше следующим образом:

import React, { useState, useEffect } from «react»;

//...

useEffect (() => {

document.title = name + «from «+ location;

}) ;

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

Название класса React Hooks

Добавление кода очистки

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

import React from «react»;

export default class ClassDemo extends React.Component {

constructor (props) {

super (props) ;

this.state = {

resolution: {

width: window.innerWidth,

height: window.innerHeight

}

};

this.handleResize = this.handleResize.bind (this) ;

}

componentDidMount () {

window.addEventListener («resize», this.handleResize) ;

}

componentDidUpdate () {

window.addEventListener («resize», this.handleResize) ;

}

componentWillUnmount () {

window.removeEventListener (‘resize’, this.handleResize) ;

}

handleResize () {

this.setState ({

resolution: {

width: window.innerWidth,

height: window.innerHeight

}

}) ;

}

render () {

return (

 

 

 

{this.state.resolution.width} x {this.state.resolution.height}

 

 

)

}

}

Приведенный выше код отобразит текущее разрешение окна вашего браузера. Измените размер окна, и вы увидите, что числа обновляются автоматически. Если вы нажмете F11в Chrome, он должен отобразить полное разрешение вашего монитора. Мы также использовали метод жизненного цикла componentWillUnmountдля отмены регистрации resizeсобытия.

Давайте воспроизведем приведенный выше код на основе классов в нашей версии Hook. Нам нужно определить третий useStateхук и второй useEffectхук для обработки этой новой функции:

import React, { useState, useEffect } from «react»;

export default function HookDemo (props) {

...

const [resolution, setResolution] = useState ({

width: window.innerWidth,

height: window.innerHeight

}) ;

useEffect (() => {

const handleResize = () => {

setResolution ({

width: window.innerWidth,

height: window.innerHeight

}) ;

};

window.addEventListener («resize», handleResize) ;

// return clean-up function

return () => {

document.title = ‘React Hooks Demo’;

window.removeEventListener («resize», handleResize) ;

};

}) ;

...

return (

 

 

...

 

{resolution.width} x {resolution.height}

 

 

) ;

}

Разрешение классов React Hooks

Удивительно, но эта версия кода Hook делает то же самое. Он чище и компактнее. Преимущество помещения кода в собственное useEffectобъявление заключается в том, что мы можем легко его протестировать, поскольку код находится в изоляции.

Вы заметили, что мы возвращаем функцию в этом useEffectхуке? Это связано с тем, что любая функция, которую вы возвращаете внутри useEffectфункции, будет считаться кодом для очистки. Если вы не вернете функцию, очистка не будет выполнена. В этом случае требуется очистка, так как в противном случае вы столкнетесь с сообщением об ошибке, зарегистрированным в консоли вашего браузера, в котором говорится: «Невозможно выполнить обновление состояния React для несмонтированного компонента».

Пользовательские хуки React

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

Мы сделаем это, извлекая resizeфункциональность и помещая ее за пределы нашего компонента.

Создайте новую функцию следующим образом:

function useWindowResolution () {

const [width, setWidth] = useState (window.innerWidth) ;

const [height, setHeight] = useState (window.innerHeight) ;

useEffect (() => {

const handleResize = () => {

setWidth (window.innerWidth) ;

setHeight (window.innerHeight) ;

};

window.addEventListener («resize», handleResize) ;

return () => {

window.removeEventListener («resize «, handleResize) ;

};

}, [width, height]) ;

return {

width,

height

};

}

Далее в компоненте вам нужно будет заменить этот код:

const [resolution, setResolution] = useState ({

width: window.innerWidth,

height: window.innerHeight

}) ;

...с этим:

const resolution = useWindowResolution () ;

Удалите второй useEffectкод. Сохраните файл и протестируйте его. Все должно работать так же, как и раньше.

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

function useDocumentTitle (title) {

useEffect (() => {

document.title = title;

}) ;

}

Наконец, вызовите его из компонента:

useDocumentTitle (name + «from «+ location) ;

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

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

Начнем с пользовательского хука. Добавьте следующее вне компонента:

function useFormInput (initialValue) {

const [value, setValue] = useState (initialValue) ;

function handleChange (e) {

setValue (e.target.value) ;

}

return {

value,

onChange: handleChange

};

}

Затем обновите компонент, чтобы использовать его:

export default function HookDemo (props) {

const name = useFormInput («Agata») ;

const location = useFormInput («Nairobi») ;

const resolution = useWindowResolution () ;

useDocumentTitle (name.value + «from «+ location.value) ;

return (

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Hello {name.value} from {location.value}

 

{resolution.width} x {resolution.height}

 

 

) ;

}

Медленно просмотрите код и определите все изменения, которые мы сделали. Довольно аккуратно, правда? Наш компонент намного компактнее.

Для целей этого руководства мы объявляем наши хуки как функции в том же файле, что и компонент, который их использует. Однако в обычном проекте React у вас будет hooksпапка с каждым из этих хуков в отдельном файле, который затем можно будет импортировать в любое место, где это необходимо.

Мы могли бы даже упаковать хуки useFormInputи во useDocumentTitleвнешний useWindowResolutionмодуль npm, поскольку они полностью независимы от основной логики нашего кода. Мы можем легко повторно использовать эти пользовательские хуки в других частях проекта или даже в других проектах в будущем.

Для справки, вот полная версия компонента Hooks:

import React, { useState, useEffect } from «react»;

function useWindowResolution () {

const [width, setWidth] = useState (window.innerWidth) ;

const [height, setHeight] = useState (window.innerHeight) ;

useEffect (() => {

const handleResize = () => {

setWidth (window.innerWidth) ;

setHeight (window.innerHeight) ;

};

window.addEventListener («resize», handleResize) ;

return () => {

window.removeEventListener («resize «, handleResize) ;

};

}, [width, height]) ;

return {

width,

height

};

}

function useDocumentTitle (title) {

useEffect (() => {

document.title = title;

}) ;

}

function useFormInput (initialValue) {

const [value, setValue] = useState (initialValue) ;

function handleChange (e) {

setValue (e.target.value) ;

}

return {

value,

onChange: handleChange

};

}

export default function HookDemo (props) {

const name = useFormInput («Agata») ;

const location = useFormInput («Nairobi») ;

const resolution = useWindowResolution () ;

useDocumentTitle (name.value + «from «+ location.value) ;

return (

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Hello {name.value} from {location.value}

 

{resolution.width} x {resolution.height}

 

 

) ;

}

Компонент Hook должен отображаться и вести себя точно так же, как версия компонента класса:

Окончательный ответ на крючки

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

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

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

Давайте рассмотрим пример того, как вы можете получать данные из REST JSON API, используя Axios и React Hooks. Если вы делаете это дома, вам необходимо установить библиотеку Axios:

npm i axios

Измените свой компонент, чтобы он выглядел так:

import React, { useState, useEffect } from ‘react’;

import axios from ‘axios’;

export default function UserList () {

const [users, setUsers] = useState ([]) ;

useEffect (() => {

const fetchData = async () => {

const result = await axios (‘https: //jsonplaceholder.typicode.com/users’) ;

setUsers (result.data) ;

};

fetchData () ;

}, []) ;

const userRows = users.map ((user, index) =>

  • {user.name}
  • ) ;

    return (

     

     

    List of Users

    • {userRows}

     

     

    ) ;

    }

    Мы должны ожидать следующий вывод:

    список пользователей

    Вышеприведенный код можно реорганизовать, создав собственный пользовательский хук таким образом, чтобы нам больше не нужно было использовать useStateхуки useEffect. К счастью для нас, многие разработчики уже выполнили этот квест и опубликовали свою работу в виде пакета, который мы можем установить в нашем проекте. Мы будем использовать хуки axios от Simone Busoli, которые оказались самыми популярными.

    Вы можете установить пакет с помощью команды:

    npm i axios-hooks

    Ниже я рефакторил приведенный выше код, используя axios-hooks:

    import React from ‘react’;

    import useAxios from ‘axios-hooks’;

    export default function UserListAxiosHooks () {

    const [{ data, loading, error }, refetch] = useAxios (

    ‘https: //jsonplaceholder.typicode.com/users’

    ) ;

    if (loading) return Loading...;

    if (error) return Error! ;

    const userRows = data.map ((user, index) =>
  • {user.name}
  • ) ;

    return (

     

     

    List of Users

    • {userRows}

     

     

    ) ;

    }

    Мы не только избавились от хуков useStateи useEffectиз нашего кода, но также получили три новые способности без дополнительных умственных усилий с нашей стороны:

    для отображения статуса загрузки

    для отображения сообщений об ошибках

    для обновления данных одним нажатием кнопки

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

    Демо

    Ниже приведена живая демонстрация того, чего мы достигли на данный момент:

    Официальные хуки React

    Это основные хуки React, с которыми вы столкнетесь в своих повседневных проектах React:

    useState: для управления локальным состоянием

    useEffect: заменяет функции жизненного цикла

    useContext: позволяет вам легко работать с React Context API (решая проблему сверления пропеллеров)

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

    useReducer: расширенная версия useStateдля управления сложной логикой состояния. Это очень похоже на Redux.

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

    useMemo: возвращает значение из запомненной функции. Аналогично тому, computedесли вы знакомы с Vue.

    useRef: возвращает изменяемый объект ссылки, который сохраняется в течение всего срока службы компонента.

    useImperativeHandle: настраивает значение экземпляра, которое предоставляется родительским компонентам при использовании ref.

    useLayoutEffect: аналогично useEffect, но срабатывает синхронно после всех мутаций DOM.

    useDebugValue: отображает метку для пользовательских хуков в инструментах разработчика React.

    Все об этих хуках вы можете прочитать в официальной документации React.

    Резюме

    Сообщество React положительно отреагировало на новую функцию React Hooks. Уже существует репозиторий с открытым исходным кодом под названием awesome-react-hooks, и сотни пользовательских React Hooks были отправлены в этот репозиторий. Вот краткий пример одного из этих хуков для хранения значений в локальном хранилище:

    import useLocalStorage from «@rehooks/local-storage»;

    function MyComponent () {

    let name = useLocalStorage («name») ; // send the key to be tracked.

    return (

     

     

    {name}

     

     

    ) ;

    }

    Вам нужно будет установить local-storageхук с npm или пряжей, чтобы использовать его:

    npm i @rehooks/local-storage

    Довольно аккуратно, правда?

    Внедрение React Hooks произвело большой фурор. Его волны вышли за пределы сообщества React в мир JavaScript. Это связано с тем, что хуки — это новая концепция, которая может принести пользу всей экосистеме JavaScript. На самом деле, команда Vue.js недавно выпустила что-то похожее под названием Composition API.

    Также говорят о том, что React Hooks и Context API свергнут Redux с его трона управления состоянием. Очевидно, что хуки значительно упростили программирование и изменили способ написания нового кода. Если вы похожи на меня, у вас, вероятно, есть сильное желание переписать все ваши классы компонентов React и заменить их функциональными компонентами Hooks.

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

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

    Удачного кодирования, и дайте мне знать, как у вас дела!

3D-печать5GABC-анализAndroidAppleAppStoreAsusCall-центрChatGPTCRMDellDNSDrupalExcelFacebookFMCGGoogleHuaweiInstagramiPhoneLinkedInLinuxMagentoMicrosoftNvidiaOpenCartPlayStationPOS материалPPC-специалистRuTubeSamsungSEO-услугиSMMSnapchatSonyStarlinkTikTokTwitterUbuntuUp-saleViasatVPNWhatsAppWindowsWordPressXiaomiYouTubeZoomАвдеевкаАктивные продажиАкцияАлександровск ЛНРАлмазнаяАлчевскАмвросиевкаАнализ конкурентовАнализ продажАнтимерчандайзингАнтрацитАртемовскАртемовск ЛНРАссортиментная политикаБелгородБелицкоеБелозерскоеБердянскБизнес-идеи (стартапы)БрендБрянкаБукингВахрушевоВендорВидеоВикипедияВирусная рекламаВирусный маркетингВладивостокВнутренние продажиВнутренний маркетингВолгоградВолновахаВоронежГорловкаГорнякГорскоеДебальцевоДебиторкаДебиторская задолженностьДезинтермедитацияДзержинскДивизионная система управленияДизайнДимитровДирект-маркетингДисконтДистрибьюторДистрибьюцияДобропольеДокучаевскДоменДружковкаЕкатеринбургЕнакиевоЖдановкаЗапорожьеЗимогорьеЗолотоеЗоринскЗугрэсИжевскИловайскИрминоКазаньКалининградКировскКировскоеКомсомольскоеКонстантиновкаКонтент-маркетингКонтент-планКопирайтингКраматорскКрасноармейскКрасногоровкаКраснодарКраснодонКраснопартизанскКрасный ЛиманКрасный ЛучКременнаяКураховоКурскЛисичанскЛуганскЛутугиноМакеевкаМариупольМаркетингМаркетинговая информацияМаркетинговые исследованияМаркетинговый каналМаркетинг услугМаркетологМарьинкаМедиаМелекиноМелитопольМенеджментМерчандайзерМерчандайзингМиусинскМолодогвардейскМоскваМоспиноНижний НовгородНиколаевНиколаевкаНишевой маркетингНовоазовскНовогродовкаНоводружескНовосибирскНумерическая дистрибьюцияОдессаОмскОтдел маркетингаПартизанский маркетингПервомайскПеревальскПетровскоеПлата за кликПоисковая оптимизацияПопаснаяПравило ПаретоПривольеПрогнозирование продажПродвижение сайтов в ДонецкеПроизводство видеоПромоПромоушнПрямой маркетингРабота для маркетологаРабота для студентаРазработка приложенийРаспродажаРегиональные продажиРекламаРеклама на асфальтеРемаркетингРетро-бонусРибейтРитейлРовенькиРодинскоеРостов-на-ДонуРубежноеСамараСанкт-ПетербургСаратовСватовоСвердловскСветлодарскСвятогорскСевастопольСеверодонецкСеверскСедовоСейлз промоушнСелидовоСимферопольСинергияСколковоСлавянскСнежноеСоздание сайтов в ДонецкеСоледарСоциальные сетиСочиСтаробельскСтаробешевоСтахановСтимулирование сбытаСуходольскСчастьеТелемаркетингТельмановоТираспольТорговый представительТорезТрейд маркетингТрейд промоушнТюменьУглегорскУгледарУкраинскХабаровскХарцызскХерсонХостингЦелевая аудиторияЦифровой маркетингЧасов ЯрЧелябинскШахтерскЮжно-СахалинскЮнокоммунаровскЯндексЯсиноватая