Руководство по получению данных

На чтение 15 мин. Просмотров 22 Опубликовано 03.11.2022

Цель этой заметки — показать вам способ выполнения HTTP GET-запросов с помощью React и пользовательского хука.

🚨 Примечание: Этот пост требует от вас знания основ React (основные хуки и fetch-запросы).

Любой вид обратной связи приветствуется, спасибо и я надеюсь, что вам понравится статья.🤗

Содержание

  1. Оглавление
  2. 🚨 Используемые технологии.
  3. 〽️ Создание проекта.
  4. 〽️ Первые шаги.
  5. 〽️ Делаем наш первый фетч.
  6. 〽️ Отображение данных API на экране.
  7. 〽️ Создание пользовательского крючка.
  8. 〽️ Улучшение хука useFetch.
  9. 〽️ Добавление новых компонентов и рефакторинг.
  10. 🟡 Header.tsx
  11. 🟡 Loading.tsx
  12. 🟡 ErrorMessage.tsx
  13. 🟡 Card.tsx
  14. 🟡 LayoutCards.tsx
  15. Franklin361 / fetching-data-custom-hook
  16. Руководство по получению данных и созданию пользовательского хука
  17. Получение данных и создание пользовательских Hook

Оглавление

📌 Технологии для использования

📌 Создание проекта

📌 Первые шаги

📌 Делаем наш первый забор

📌 Отображение данных API на экране

📌 Создание пользовательского крючка

📌 Усовершенствование крючка useFetch.

📌 Добавление новых компонентов и рефакторинг

📍 Header.tsx

📍 Loading.tsx

📍 ErrorMessage.tsx

📍 Card.tsx

📍 LayoutCards.tsx


🚨 Используемые технологии.

▶️ React JS (версия 18)

▶️ Vite JS

▶️ TypeScript

▶️ Rick and Morty API

▶️ Vanilla CSS (стили можно найти в репозитории в конце этого поста)
 

〽️ Создание проекта.

npm init vite@latest

Войдите в полноэкранный режим Выход из полноэкранного режима

В данном случае мы назовем его: fetching-data-custom-hook (необязательно).

Выберите React, а затем TypeScript.

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

cd fetching-data-custom-hook

Войдите в полноэкранный режим Выход из полноэкранного режима

Далее мы устанавливаем зависимости:

npm install

Войдите в полноэкранный режим Выход из полноэкранного режима

Затем открываем проект в редакторе кода (в моем случае VS code)

code .

Войдите в полноэкранный режим Выход из полноэкранного режима

〽️ Первые шаги.

Внутри папки src/App.tsx мы удалим все содержимое файла и разместим функциональный компонент, который показывает заголовок и подзаголовок.

const App = () => {
  return (
            <h1 className="title">Fetching data and create custom Hook</h1>
      <span className="subtitle">using Rick and Morty API</span>
  )
}
export default App;

Войдите в полноэкранный режим Выход из полноэкранного режима

Прежде всего, мы создадим пару интерфейсов, которые помогут нам автоматически заполнять свойства, поступающие в JSON-ответе, предоставляемом API.

  • Первый интерфейс Response содержит свойство results, которое представляет собой массив Results.
  • Второй интерфейс Result, содержит только 3 свойства (хотя их больше, вы можете проверить документацию API), выбираем ID, имя и изображение персонажа.
interface Response {
  results: Result[]
}

interface Result {
  id: number;
  name: string;
  image: string;
}

Войдите в полноэкранный режим Выход из полноэкранного режима

〽️ Делаем наш первый фетч.

  1. Сначала мы добавляем состояние, которое имеет тип Result[], а значением по умолчанию будет пустой массив, поскольку мы еще не выполнили вызов API. Он будет использоваться для хранения данных API для отображения.
const App = () => {

  const [data, setData] = useState<Result[]>([]);

  return (
        <h1 className="title">Fetching data and create custom Hook</h1>
      <span className="subtitle">using Rick and Morty API</span>
  )
}
export default App;

Войдите в полноэкранный режим Выход из полноэкранного режима

  1. Чтобы выполнить выборку данных, нам нужно сделать это в useEffect, так как нам нужно выполнить выборку при первом отображении нашего компонента.

Поскольку нам нужно, чтобы он выполнялся только один раз, мы помещаем пустой массив (т.е. без каких-либо зависимостей).

const App = () => {
  const [data, setData] = useState<Result[]>([]);

    useEffect(()=> {

    },[]) // arreglo vació

  return (
    <div>
      <h1 className="title">Fetching data and create custom Hook</h1>
      <span className="subtitle">using Rick and Morty API</span>
    </div>
  )
}
export default App;

Войдите в полноэкранный режим Выход из полноэкранного режима

  1. Внутри тела функции useEffect будет сделан вызов API, а поскольку useEffect не позволяет нам использовать асинхронный код напрямую, мы сделаем вызов, используя обещания.
const [data, setData] = useState<Result[]>([]);

useEffect(()=> {
   fetch('https://rickandmortyapi.com/api/character/?page=8')
   .then( res => res.json())
   .then( (res: Response) => {})
   .catch(console.log)   
},[])

Войдите в полноэкранный режим Выход из полноэкранного режима

  1. Как только обещания будут разрешены, мы получим данные, соответствующие API, которые установим в состояние с помощью функции setData.

Теперь мы можем вывести данные на экран. 😌

🚨 Если что-то пойдет не так с API, catch поймает ошибку и покажет ее в консоли, а значение статуса «data» останется пустым массивом (и в конце не будет показано ничего, кроме названия и подзаголовка приложения).

const [data, setData] = useState<Result[]>([]);

useEffect(()=> {
   fetch('https://rickandmortyapi.com/api/character/?page=8')
   .then( res => res.json())
   .then( (res: Response) =>  {
      setData(res.results);
   })
   .catch(console.log)   
},[])

Войдите в полноэкранный режим Выход из полноэкранного режима

〽️ Отображение данных API на экране.

Перед отображением данных API нам необходимо выполнить оценку. 🤔

🔵 Только если длина значения статуса «data» больше 0, мы выводим данные API на экран.

🔵 Если длина значения статуса «data» меньше или равна 0, на экране не будет отображаться никаких данных, только название и субтитр.

const App = () => {
    const [data, setData] = useState<Result[]>([]);

    useEffect(()=> {
       fetch('https://rickandmortyapi.com/api/character/?page=8')
       .then( res => res.json())
       .then( (res: Response) =>  {
          setData(res.results);
       })
       .catch(console.log)   
    },[])

  return (
    <div>
      <h1 className="title">Fetching data and create custom Hook</h1>
      <span className="subtitle">using Rick and Morty API</span>
      {
        (data.length > 0) && <p>data</p>
      }
    </div>
  )
}
export default App;

Войдите в полноэкранный режим Выход из полноэкранного режима

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

Использование функции map, применяемой в массивах. Мы пройдем по массиву значения состояния «data» и вернем новый JSX-компонент, который в данном случае будет представлять собой только изображение и текст.

ПРИМЕЧАНИЕ: свойство key внутри div — это идентификатор, который React использует в списках для более эффективного отображения компонентов. Важно установить его.

const App = () => {
    const [data, setData] = useState<Result[]>([]);

    useEffect(()=> {
       fetch('https://rickandmortyapi.com/api/character/?page=8')
       .then( res => res.json())
       .then( (res: Response) =>  {
          setData(res.results);
       })
       .catch(console.log)   
    },[])

  return (
    <div>
      <h1 className="title">Fetching data and create custom Hook</h1>
      <span className="subtitle">using Rick and Morty API</span>
      {
        (data.length > 0) && data.map( ({ id, image, name }) => (
          <div key={id}> 
            <img src={image} alt={image} /> 
            <p>{name}</p> 
          </div>
        ))
      }
    </div>
  )
}
export default App;

Войдите в полноэкранный режим Выход из полноэкранного режима

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


 

〽️ Создание пользовательского крючка.

В папке src/hook мы создаем файл под названием useFetch.

Создайте функцию и вырежьте логику из компонента App.tsx.

const App = () => {

  return (
    <div>
      <h1 className="title">Fetching data and create custom Hook</h1>
      <span className="subtitle">using Rick and Morty API</span>
      {
        (data.length > 0) && data.map( ({ id, image, name }) => (
          <div key={id}> 
            <img src={image} alt={image} /> 
            <p>{name}</p> 
          </div>
        ))
      }
    </div>
  )
}
export default App;

Войдите в полноэкранный режим Выход из полноэкранного режима

Вставьте логику в эту функцию, а в конце верните значение состояния «data«.

export const useFetch = () => {
  const [data, setData] = useState<Result[]>([]);

  useEffect(()=> {
     fetch('https://rickandmortyapi.com/api/character/?page=8')
     .then( res => res.json())
     .then( (res: Response) =>  {
        setData(res.results);
     })
     .catch(console.log)   
  },[]);

  return {
    data
  }
}

Войдите в полноэкранный режим Выход из полноэкранного режима

Наконец, мы вызываем хук useFetch, извлекающий данные.

Вот и все, наш компонент стал еще чище и легче для чтения. 🤓

const App = () => {

  const { data } = useFetch();

  return (
    <div>
      <h1 className="title">Fetching data and create custom Hook</h1>
      <span className="subtitle">using Rick and Morty API</span>
      {
        (data.length > 0) && data.map( ({ id, image, name }) => (
          <div key={id}> 
            <img src={image} alt={image} /> 
            <p>{name}</p> 
          </div>
        ))
      }
    </div>
  )
}
export default App;

Войдите в полноэкранный режим Выход из полноэкранного режима

Но подождите, мы еще можем улучшить этот крючок. 🤯
 

〽️ Улучшение хука useFetch.

Теперь мы улучшим крючок, добавив больше свойств.

К существующему состоянию мы добавим другие свойства, и это новое состояние будет иметь тип DataState.

interface DataState {
    loading: boolean;
    data: Result[];
    error: string | null;
}

Войдите в полноэкранный режим Выход из полноэкранного режима

loading, булево значение, позволит нам узнать, когда выполняется вызов API. По умолчанию значение будет установлено в true.

error, строка или нулевое значение, то будет показано сообщение об ошибке. По умолчанию значение будет равно null.

🔵 data, значение типа Result[] , покажет данные API. По умолчанию значением будет пустой массив.

🔴 ПРИМЕЧАНИЕ: только что переименовал свойства поместья.

🔵 данные ➡️ dataState

🔵 setData ➡️ setDataState

export const useFetch = () => {
    const [dataState, setDataState] = useState<DataState>({
      data: [],
      loading: true,
      error: null
  });

  useEffect(()=> {
     fetch('https://rickandmortyapi.com/api/character/?page=8')
     .then( res => res.json())
     .then( (res: Response) =>  {
        setData(res.results);
     })
     .catch(console.log)   
  },[]);

  return {
    data
  }
}

Войдите в полноэкранный режим Выход из полноэкранного режима

Теперь мы вынесем логику useEffect в отдельную функцию. Эта функция будет называться handleFetch.

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

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

const handleFetch = useCallback(
    () => {},
    [],
)

Войдите в полноэкранный режим Выход из полноэкранного режима

Функция, полученная в useCallback, может быть асинхронной, поэтому мы можем использовать async/await.

  1. Сначала мы разместим try/catch для обработки ошибок.
  2. Затем мы создаем константу со значением URL для вызова API.
  3. Мы выполняем вызов API с помощью fetch и отправляем ему URL (функция await позволит нам дождаться ответа, правильного или неправильного, в случае ошибки он сразу перейдет в функцию catch).
const handleFetch = useCallback(
      async () => {
          try {
                            const url = 'https://rickandmortyapi.com/api/character/?page=18';
              const response = await fetch(url);
          } catch (error) {}
        },
        [],
    )

Войдите в полноэкранный режим Выход из полноэкранного режима

  1. Затем мы оцениваем ответ, если есть ошибка, то активируем catch и отправляем ошибку, которую выдает нам API.
  2. В catch мы собираемся установить состояние. Мы вызываем setDataState, передаем ей функцию для получения предыдущих значений (prev). Мы возвращаем следующее.
    1. Мы разбрасываем предыдущие свойства (…prev), которые в данном случае будут только значением свойства data, которое в итоге окажется пустым массивом.
    2. загрузки, мы устанавливаем значение false.
    3. error, мы устанавливаем значение параметра error, который получает catch, чтобы получить сообщение и поместить его в это свойство.
const handleFetch = useCallback(
      async () => {
          try {
                            const url = 'https://rickandmortyapi.com/api/character/?page=18';
              const response = await fetch(url);

              if(!response.ok) throw new Error(response.statusText);

          } catch (error) {

              setDataState( prev => ({
                  ...prev,
                  loading: false,
                  error: (error as Error).message
              }));
          }
        },
        [],
    )

Войдите в полноэкранный режим Выход из полноэкранного режима

  1. Если нет ошибки от API, мы получаем информацию и устанавливаем состояние аналогично тому, как мы это делали в catch.
  2. Мы вызываем setDataState, передаем ей функцию для получения предыдущих значений (prev). Мы возвращаем следующее.
    1. Мы распространяем предыдущие свойства (…prev), которые в данном случае будут только значением свойства error, которое в итоге будет равно null.
    2. загрузки, мы устанавливаем значение false.
    3. данных, будет значение счетчика dataApi путем обращения к его свойству results.
const handleFetch = useCallback(
      async () => {
          try {
                            const url = 'https://rickandmortyapi.com/api/character/?page=18';
              const response = await fetch(url);

              if(!response.ok) throw new Error(response.statusText);

              const dataApi: Response = await response.json();

              setDataState( prev => ({
                  ...prev,
                  loading: false,
                  data: dataApi.results
              }));

          } catch (error) {

              setDataState( prev => ({
                  ...prev,
                  loading: false,
                  error: (error as Error).message
              }));
          }
        },
        [],
    )

Войдите в полноэкранный режим Выход из полноэкранного режима

После создания функции handleFetch мы возвращаемся к функции useEffect, удаляем логику и добавляем следующее.

Мы оцениваем, если значение состояния «dataState», обращаясь к свойству data, содержит длину, равную 0, то мы хотим, чтобы функция была выполнена. Это делается для того, чтобы избежать вызова функции более одного раза.

useEffect(() => {
    if (dataState.data.length === 0) handleFetch();
}, []);

Войдите в полноэкранный режим Выход из полноэкранного режима

И крючок будет выглядеть следующим образом:

🔴 ПРИМЕЧАНИЕ: в конце хука мы возвращаем, используя оператор spread, значение состояния «dataState».

ПРИМЕЧАНИЕ: интерфейсы были перемещены в соответствующую папку внутри src/interfaces.

import { useState, useEffect, useCallback } from 'react';
import { DataState, Response } from '../interface';

const url = 'https://rickandmortyapi.com/api/character/?page=18';

export const useFetch = () => {

    const [dataState, setDataState] = useState<DataState>({
        data: [],
        loading: true,
        error: null
    });

    const handleFetch = useCallback(
        async () => {
            try {
                const response = await fetch(url);

                if(!response.ok) throw new Error(response.statusText);

                const dataApi: Response = await response.json();

                setDataState( prev => ({
                    ...prev,
                    loading: false,
                    data: dataApi.results
                }));

            } catch (error) {

                setDataState( prev => ({
                    ...prev,
                    loading: false,
                    error: (error as Error).message
                }));
            }
        },
        [],
    )

    useEffect(() => {
        if (dataState.data.length === 0) handleFetch();
    }, []);

    return {
        ...dataState
    }
}

Войдите в полноэкранный режим Выход из полноэкранного режима

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

〽️ Добавление новых компонентов и рефакторинг.

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

Внутри папки components мы создаем следующие файлы.
 

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

export const Header = () => {
    return (
        <>
            <h1 className="title">Fetching data and create custom Hook</h1>
            <span className="subtitle">using Rick and Morty API</span>
        </>
    )
}

Войдите в полноэкранный режим Выход из полноэкранного режима

🟡 Loading.tsx

Этот компонент будет показан только в том случае, если свойство load хука установлено в true. ⏳

export const Loading = () => {
  return (
    <p className='loading'>Loading...</p>
  )
}

Войдите в полноэкранный режим Выход из полноэкранного режима


 

🟡 ErrorMessage.tsx

Этот компонент будет отображаться только в том случае, если свойство error хука содержит строковое значение. 🚨

export const ErrorMessage = ({msg}:{msg:string}) => {
  return (
    <div className="error-msg">{msg.toUpperCase()}</div>
  )
}

Войдите в полноэкранный режим Выход из полноэкранного режима


 

🟡 Card.tsx

Отображает данные API, т.е. изображение и его текст. 🖼️

import { Result } from '../interface';

export const Card = ({ image, name }:Result) => {

    return (
        <div className='card'>
            <img src={image} alt={image} width={100} />
            <p>{name}</p>
        </div>
    )
}

Войдите в полноэкранный режим Выход из полноэкранного режима

🟡 LayoutCards.tsx

Этот компонент служит в качестве контейнера для компоновки свойства данных и отображения карточек с информацией о них. 🔳

ПРИМЕЧАНИЕ: мы используем memo, заключая наш компонент, чтобы избежать повторного рендеринга, что, вероятно, не будет замечено в данном приложении, но это просто совет. Эта мемо-функция перерисовывается только в том случае, если свойство «data» меняет свои значения.

import { memo } from "react"
import { Result } from "../interface"
import { Card } from "./"

interface Props { data: Result[] }

export const LayoutCards = memo(({data}:Props) => {
    return (
        <div className="container-cards">
            {
                (data.length > 0) && data.map( character => (
                    <Card {...character} key={character.id}/>
                ))
            }
        </div>
    )
})

Войдите в полноэкранный режим Выход из полноэкранного режима

Вот как будет выглядеть наш компонент App.tsx.

Мы создаем функцию showData и оцениваем:

  • Если свойство loading равно true, мы возвращаем компонент <Loading/>.
  • Если свойство error равно true, мы возвращаем компоненту <ErrorMessage/>, отправляя ошибку в компонент.
  • Если ни одно из условий не выполняется, это означает, что данные API готовы, и мы возвращаем компонент <LayoutCards/> и отправляем данные на отображение.

Наконец, под компонентом мы раскрываем скобки и вызываем функцию showData.

import { ErrorMessage, Header, Loading, LayoutCards } from './components'
import { useFetch } from './hook';

const App = () => {

  const { data, loading, error } = useFetch();

  const showData =  () => {
    if (loading) return <Loading/>
    if (error) return <ErrorMessage msg={error}/>
    return <LayoutCards data={data} />
  }

  return (
    <>
      <Header/>
      { showData() }
    </>
  )
}
export default App;

Войдите в полноэкранный режим Выход из полноэкранного режима

ПРИМЕЧАНИЕ: Вы также можете перенести функцию showData в хук и изменить расширение файла хука на .tsx, это связано с тем, что вы используете JSX при возврате различных компонентов.


Спасибо, что дошли так далеко. 🙌

Я оставлю репозиторий, чтобы вы могли взглянуть на него, если захотите. ⬇️

Franklin361 / fetching-data-custom-hook

Руководство по получению данных и созданию пользовательского хука

Получение данных и создание пользовательских Hook

Руководство по получению данных и созданию пользовательского хука

Просмотр на GitHub

На чтение 28 мин Просмотров 2к. Опубликовано 31.05.2021

Создание интерфейсных приложений CRUD часто начинается легко, а затем становится сложным по мере того, как вы продолжаете добавлять функции. Для каждой конечной точки API вам нужно будет иметь дело с управлением состоянием, синхронизацией, кэшированием и обработкой ошибок. В этой статье вы узнаете о библиотеке React Query и о том, как она может помочь решить все эти проблемы. Библиотека описывает себя как «отсутствующую библиотеку выборки данных», обеспечивающую «управление состоянием сервера» для React.

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

Содержание

  1. О React Query 3
  2. Предпосылки
  3. О проекте
  4. Настройка проекта
  5. Установка React Query
  6. Компоненты пользовательского интерфейса
  7. Базовый запрос
  8. Запрос отдельной записи
  9. Devtools
  10. Конфигурация
  11. Запросы с разбивкой на страницы
  12. Бесконечные запросы
  13. Мутации
  14. Заключение

О React Query 3

React Query — это проект с открытым исходным кодом, созданный Таннером Линси. Последняя основная версия, React Query 3, была официально выпущена в декабре 2020 года. В этой новой версии были добавлены новые функции и улучшены существующие.

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

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

Предпосылки

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

  • Реагировать
  • React Router
  • Реагировать на хуки
  • Получение данных REST API

В машинной среде вашего разработчика вам необходимо настроить следующее:

  • Node.js
  • Git
  • Клиент REST, такой как Postman, Insomnia или расширение REST VS Code.

Разобравшись с этим, давайте приступим к настройке демонстрационного проекта.

О проекте

Демо-проект, который мы будем анализировать, представляет собой интерфейсное приложение React, которое отображает данные, предоставленные сервером REST JSON API. Приложение состоит всего из пяти страниц, которые демонстрируют функции React Query, о которых мы будем изучать. Эти функции включают:

  • Базовый запрос
  • Запрос с разбивкой на страницы
  • Бесконечный запрос
  • Создать мутацию
  • Обновить мутацию
  • Удалить мутацию

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

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

Настройка проекта

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

  • Vite : очень быстрый инструмент для сборки
  • WindiCSS : очень быстрый компилятор Tailwind CSS
  • React Hook Form : конструктор форм и библиотека проверки с использованием React-хуков
  • React Modal : доступный модальный компонент
  • Axios : HTTP-клиент на основе обещаний для браузеров
  • Сервер JSON : полный поддельный сервер REST API

Чтобы настроить приложение React Query Demo на вашем компьютере, выполните следующие инструкции:

# Clone the project
git clone git@github.com:sitepoint-editors/react-query-demo.git

# Navigate to project directory
cd react-query-demo

# Install package dependencies
npm install

# Setup database file for `json-server`
cp api/sample.db.json api/db.json

# Start the `json-server`
npm run json-server

Файл базы данных, используемый объектом, json-serverсодержит массив пользователей. Когда вы выполняете npm run json-server, на порту запускается поддельный сервер API 3004. Вы можете получить доступ к данным пользователей через. Выполнение запроса GET даст следующий пример ответа JSON:

[
  {
    "id": 1,
    "first_name": "Siffre",
    "last_name": "Timm",
    "email": "stimmes0@nasa.govz",
    "gender": "Male"
  },
  {
    "id": 2,
    "first_name": "Fonzie",
    "last_name": "Coggen",
    "email": "fcoggen1@weather.com",
    "gender": "Female"
  },
  {
    "id": 3,
    "first_name": "Shell",
    "last_name": "Kos",
    "email": "skos2@prweb.com",
    "gender": "Female"
  }
]

Затем запустите сервер разработки, который будет запускать интерфейсный код:

# In another terminal, start the React dev server
npm run dev

Перейдите в свой браузер и откройте http: // localhost: 3000, чтобы получить доступ к приложению. У вас должно быть такое же впечатление, как показано на превью выше. Убедитесь, что вы выполняете следующие задачи, чтобы тщательно изучить возможности приложения:

  • Просмотрите страницу базового запроса (домашняя страница).
  • Посетите страницу с разбивкой на страницы и используйте кнопки » Назад» и » Далее».
  • Посетите страницу Infinite и взаимодействуйте с кнопкой Загрузить еще.
  • Вернитесь на страницу базового запроса и нажмите кнопку » Создать пользователя». Вы будете перенаправлены на страницу создания пользователя. Заполните форму и нажмите кнопку Сохранить.
  • В таблице пользователей найдите значок » Изменить». Нажмите здесь. Вы попадете на страницу редактирования пользователя. Внесите любые изменения, которые вам нравятся, затем нажмите кнопку » Сохранить».
  • В таблице пользователей найдите значок Удалить. Нажмите здесь. Откроется модальное диалоговое окно с просьбой подтвердить действие удаления. Нажмите кнопку » Удалить«, чтобы подтвердить.

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

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

Установка React Query

React Query можно установить в пустой или существующий проект React с помощью следующей команды:

npm install react-query

Пакет поставляется со всем, что вам нужно, включая служебную функцию Devtools, которую мы рассмотрим в следующем разделе. После установки пакета вам необходимо обновить самый верхний компонент, — App.jsx- следующим образом:

import { QueryClient, QueryClientProvider } from "react-query";

function App() {
  const queryClient = new QueryClient();

  return (
    <QueryClientProvider client={queryClient}>
      /* place application containers/views here */
    </QueryClientProvider>
  );
}

export default App;

Любой дочерний компонент QueryClientProviderсможет получить доступ к хукам, предоставляемым библиотекой React Query. В этой статье мы будем использовать следующие хуки:

  • useQuery
  • useInfiniteQuery
  • useMutation
  • useQueryClient

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

import { QueryClient, QueryClientProvider } from "react-query";

import BasicQuery from "./views/BasicQuery";
import InfiniteQuery from "./views/InfiniteQuery";
import PaginatedQuery from "./views/PaginatedQuery";
import CreateUser from "./views/CreateUser";
import EditUser from "./views/EditUser";

function App() {
  const queryClient = new QueryClient();

  return (
    <QueryClientProvider client={queryClient}>
      <Switch>
        <Route path="/" exact>
          <BasicQuery />
        </Route>
        <Route path="/paginated">
          <PaginatedQuery />
        </Route>
        <Route path="/infinite">
          <InfiniteQuery />
        </Route>
        <Route path="/user/create">
          <CreateUser />
        </Route>
        <Route path="/user/edit/:id">
          <EditUser />
        </Route>
      </Switch>
    </QueryClientProvider>
  );
}

export default App;

Компоненты пользовательского интерфейса

Прежде чем мы перейдем к следующему разделу, я думаю, что лучше всего иметь обзор основных компонентов пользовательского интерфейса, используемых в проекте для отображения, создания и обновления пользовательских данных. Начнем с components/UserTable.jsx. Это таблица компонент отображает пользовательские данные и используется BasicQuery.jsxи PaginatedQuery.jsxстраницы. Для этого требуется одна опора, множество пользователей. Ниже представлена ​​урезанная версия готового файла:

import React, { useState, useContext } from "react";
import { Link } from "react-router-dom";
import EditIcon from "../icons/edit";
import DeleteIcon from "../icons/delete";

function UserTable({ users }) {
  const rows = users.map((user, index) => (
    <tr key={index}>
      <td>{user.id}</td>
      <td>{user.first_name}</td>
      <td>{user.last_name}</td>
      <td>{user.email}</td>
      <td>{user.gender}</td>
      <td>
        <Link to={`/user/edit/${user.id}`}>
          <EditIcon />
        </Link>
        <button onClick={() => showDeleteModal(user.id)}>
          <DeleteIcon />
        </button>
      </td>
    </tr>
  ));

  return (
    <React.Fragment>
      <div>
        <Link to="/user/create">Create User</Link>
      </div>
      <table>
        <thead>
          <tr>
            <th>Id</th>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Email</th>
            <th>Gender</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>{rows}</tbody>
      </table>
    </React.Fragment>
  );
}

Далее мы посмотрим на components/UserForm.jsx. Эта форма компонент используется views/CreateUser.jsxи views/EditUser.jsxстраницы для выполнения своих задач. Ниже представлена ​​упрощенная версия компонента:

import React from "react";
import { useForm } from "react-hook-form";
import { useHistory } from "react-router-dom";

import "./form.css";

function UserForm({ user, submitText, submitAction }) {
  const {
    register,
    formState: { errors },
    handleSubmit,
  } = useForm({
    defaultValues: user || {},
  });

  const history = useHistory();

  return (
    <div>
      <form onSubmit={handleSubmit(submitAction)}>
        {user && (
          <section className="field">
            <label htmlFor="id">User Id</label>
            <input type="text" name="id" value={user.id} disabled />
          </section>
        )}

        <section className="field">
          <div>
            <label htmlFor="first_name">First Name</label>
            <input
              type="text"
              {...register("first_name", { required: true })}
            />
            <span className="errors">
              {errors.first_name && "First name is required"}
            </span>
          </div>
          <div>
            <label htmlFor="last_name">Last Name</label>
            <input type="text" {...register("last_name", { required: true })} />
            <span className="errors">
              {errors.last_name && "Last name is required"}
            </span>
          </div>
        </section>

        <section className="field">
          <label htmlFor="email">Email</label>
          <input
            type="email"
            {...register("email", { required: true, pattern: /^S+@S+$/i })}
          />
          <span className="errors">
            {errors.email &&
              errors.email.type === "required" &&
              "Email is required"}
            {errors.email &&
              errors.email.type === "pattern" &&
              "Provide a valid email address"}
          </span>
        </section>

        <section className="field">
          <label htmlFor="gender">Gender</label>
          <select {...register("gender", { required: true })}>
            <option value=""></option>
            <option value="Male">Male</option>
            <option value="Female">Female</option>
          </select>
          <span className="errors">
            {errors.gender && "Gender is required"}
          </span>
        </section>

        <div>
          <button type="submit"> {submitText} </button>
          <button type="button" onClick={() => history.goBack()}>
            Back
          </button>
        </div>
      </form>
    </div>
  );
}

export default UserForm;

UserFormКомпонент предназначен для выполнения проверки на представленных данных пользователя. Ожидается следующий реквизит:

  • user: объект данных (необязательно)
  • submitText: текстовое значение для кнопки » Отправить»
  • submitAction: функция, обрабатывающая отправку формы

В следующем разделе мы начнем рассматривать основные функции React Query

В следующем разделе мы начнем рассматривать основные функции React Query.

Базовый запрос

Получить данные с помощью React Query довольно просто. Все, что вам нужно сделать, это определить функцию выборки, а затем передать ее в качестве параметра useQueryмутации. Вы можете увидеть пример views/BasicQuery.jsxстраницы ниже:

import React from "react";
import { useQuery } from "react-query";

import UserTable from "../components/UserTable";

function BasicQuery() {
  const fetchAllUsers = async () =>
    await (await fetch("http://localhost:3004/users")).json();

  const { data, error, status } = useQuery("users", fetchAllUsers);

  return (
    <div>
      <h2>Basic Query Example</h2>
      <div>
        {status === "error" && <div>{error.message}</div>}

        {status === "loading" && <div>Loading...</div>}

        {status === "success" && <UserTable users={data} />}
      </div>
    </div>
  );
}

export default BasicQuery;

Давайте разберемся:

  1. Сначала мы импортируем useQueryчерез оператор import { useQuery } from «react-query».
  2. Затем мы объявляем функцию обещания, fetchAllUsersкоторая извлекает данные с нашего поддельного сервера JSON API.
  3. Затем мы запускаем useQueryфункцию перехвата. Обязательны следующие параметры:
    1. ключевой запрос, который может быть либо строкой или массивом. Он используется для идентификации и отслеживания результатов запроса в целях кэширования.
    2. функция запроса, который должен возвращать обещание, что либо данные решительность или выдаст ошибку.
  4. useQueryФункция возвращает следующие переменные состояния:
    1. data: это результат функции выборки (обещания).
    2. error: если выдается ошибка, это будет установлено. В противном случае он равен нулю, если запрос на выборку успешен.
    3. status: Это строка, которая может иметь значение idle, loading, errorили success.

useQueryКрюк принимает гораздо больше параметров и возвращает намного больше переменных, которые были задокументированы в React документации Запроса. Приведенный выше пример предназначен для демонстрации минимальных настроек, необходимых для выполнения запроса API с использованием библиотеки.

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

Запрос отдельной записи

Запрос одной записи может быть выполнен с использованием аналогичного синтаксиса, который использовался в предыдущем разделе. Разница здесь в том, что:

  • вам нужно передать аргумент функции выборки через анонимную функцию
  • вам нужно уникальное имя запроса для каждой отдельной записи, что вы можете сделать с помощью массива: [queryName, {params}]
function() {
   const fetchUser = async (id) =>
    await (await fetch(`http://localhost:3004/users/${id}`)).json();

  const { data, error, status } = useQuery(["user", { id }], (id) =>
    fetchUser(id)
  );

  return (...)
}

Однако есть альтернативный способ передачи аргументов. Рассмотрим следующий код:

const { data, error, status } = useQuery(["user", { id }], fetchUser);

Используя приведенный выше синтаксис, вам необходимо изменить fetchUserфункцию для приема queryKeyобъекта следующим образом:

const fetchUser = async ({ queryKey }) => {
  const [_key, { id }] = queryKey;
  const response = await fetch(`http://localhost:3004/users/${id}`);

  if (!response.ok) {
    throw new Error(response.statusText);
  }

  return response.json();
};

Поскольку мы используем Fetch API, ответы 404 не считаются ошибками. Вот почему нам нужно написать дополнительную логику, чтобы справиться с этой ситуацией. При использовании клиентской библиотеки Axios API выполнение этой дополнительной проверки не требуется.

Посмотрите, views/EditUser.jsxкак был реализован весь код. Там есть код мутации, который мы обсудим позже в статье.

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

import { ReactQueryDevtools } from "react-query/devtools";

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      {/* The rest of your application */}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

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

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

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

Конфигурация

В приложении React Query, когда страница загружается в первый раз, библиотека извлекает данные из API, представляет их вам и затем кэширует. Когда это произойдет, вы заметите сообщение «загрузка».

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

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

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

  • cacheTime : по умолчанию 5 минут или 300000 миллисекунд.
  • staleTime : по умолчанию 0 миллисекунд

cacheTimeопределяет, как долго данные могут храниться в кэше до их удаления. staleTimeопределяет, сколько времени требуется, чтобы данные устарели. Когда данные становятся устаревшими, они помечаются для повторной выборки. Это произойдет в следующий раз, когда пользователь снова зайдет на страницу или перефокусирует окно / вкладку браузера.

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

 function Todos() {

   const result = useQuery('todos', () => fetch('/todos'), {
     staleTime: 60 * 1000 // 1 minute
     cacheTime: 60 * 1000 * 10 // 10 minutes
   })

 }

Вы также можете установить Infinityлюбое свойство. Это отключит сборку мусора cacheTimeи предотвратит устаревание данных staleTime.

Запросы с разбивкой на страницы

В примере с базовым запросом все 250 записей были загружены одновременно. Более удобный подход — разбить данные на страницы. Этого можно добиться с помощью useQueryкрючка. В предыдущих версиях React Query это делалось с помощью usePaginateQueryловушки, которая больше не доступна в React Query 3.

Реализация пагинации фактически начинается с внутреннего сервера API. К счастью для нас, json-serverесть поддержка пагинации. Чтобы получить доступ к этой функции, вам необходимо добавить следующие параметры к URL-адресу конечной точки:

  • _page: номер страницы
  • _limit: количество записей на странице

Пример: http: // localhost: 3004 / users? _Page = 5 & _limit = 10.

Давайте теперь посмотрим, как осуществляется разбивка на страницы с помощью useQueryхука:

import React, { useState } from "react";
import { useQuery } from "react-query";

const pageLimit = 15;

const fetchUsers = async (page = 1) =>
  await (
    await fetch(`http://localhost:3004/users?_page=${page}&_limit=${pageLimit}`)
  ).json();

function Users() {
  const [page, setPage] = useState(1);
  const { data } = useQuery(["paginatedUsers", page], () => fetchUsers(page), {
    keepPreviousData: true,
  });
}

Этот пример очень похож на базовый запрос, который мы рассматривали ранее. Однако есть несколько ключевых отличий:

  1. Функция обещания fetchUsersтеперь принимает целочисленный pageпараметр. Размер страницы устанавливается с помощью переменной pageLimit.
  2. useQueryКрючок подпись выглядит совсем другое:
    1. Первый параметр — это массив [«paginatedUsers», page]. Это необходимо для отслеживания данных каждой страницы отдельно.
    2. Второй параметр — анонимная функция. Он определен таким образом, чтобы передать pageаргумент fetchUsersфункции.
    3. Третий аргумент — это конфигурация объекта, в которую мы можем передать несколько настроек. В этом случае установка для keepPreviousDataсвойства значения true сообщает React Query о необходимости кэширования ранее полученных данных. По умолчанию этот параметр имеет значение false, что приводит к обновлению ранее просмотренных страниц.

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

import { useQuery, useQueryClient } from "react-query";

function Example() {
  const queryClient = useQueryClient();
  const [page, setPage] = React.useState(0);

  // Prefetch the next page!
  React.useEffect(() => {
    if (data?.hasMore) {
      queryClient.prefetchQuery(["paginatedUsers", page + 1], () =>
        fetchUsers(page + 1)
      );
    }
  }, [data, page, queryClient]);
}

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

{
  "items": [
    {
      "lives": 9,
      "type": "tabby",
      "name": "Bobby"
    },
    {
      "lives": 2,
      "type": "Ginger",
      "name": "Garfield"
    },
    ...
  ],
  "meta": {
    "itemCount": 10,
    "totalItems": 20,
    "itemsPerPage": 10,
    "totalPages": 5,
    "currentPage": 2
  },
  "links" : {
    "first": "http://cats.com/cats?limit=10",
    "previous": "http://cats.com/cats?page=1&limit=10",
    "next": "http://cats.com/cats?page=3&limit=10",
    "last": "http://cats.com/cats?page=5&limit=10"
  }
}

Обратите внимание, что в структуре тела ответа есть дополнительные метаданные, которые могут помочь в проверке кнопок нумерации страниц. При json-serverвыполнении запроса с разбивкой на страницы мы получаем следующий результат:

HTTP/1.1 200 OK
X-Powered-By: Express
Vary: Origin, Accept-Encoding
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
X-Total-Count: 250
Access-Control-Expose-Headers: X-Total-Count, Link
Link: <http://localhost:3004/users?_page=1&_limit=10>; rel="first", <http://localhost:3004/users?_page=4&_limit=10>; rel="prev", <http://localhost:3004/users?_page=6&_limit=10>; rel="next", <http://localhost:3004/users?_page=25&_limit=10>; rel="last"
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
ETag: W/"567-FwlexqEes6H/+Xt0qULv2G4aUN4"
Content-Encoding: gzip
Date: Thu, 29 Apr 2021 15:24:58 GMT
Connection: close
Transfer-Encoding: chunked

[
  {
    "id": 42,
    "first_name": "Whitby",
    "last_name": "Damrell",
    "email": "wdamrell15@i2i.jp",
    "gender": "Female"
  },
  {
    "id": 43,
    "first_name": "Fairleigh",
    "last_name": "Staner",
    "email": "fstaner16@tripod.com",
    "gender": "Female"
  },
  ...
]

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

Ниже представлена ​​урезанная версия финальной views/PaginatedQuery.jsxстраницы:

import React, { useState } from "react";
import { useQuery } from "react-query";

import UserTable from "../components/UserTable";

const pageLimit = 15;

const fetchUsers = async (page = 1) => {
  const response = await fetch(
    `http://localhost:3004/users?_page=${page}&_limit=${pageLimit}`
  );
  return response.json();
};

function PaginatedQuery() {
  const [page, setPage] = useState(1);
  const { data, isLoading, isError, status, error } = useQuery(
    ["paginatedUsers", page],
    () => fetchUsers(page),
    {
      keepPreviousData: true,
    }
  );

  const prevPage = () => {
    if (page > 1) setPage(page - 1);
  };

  const nextPage = () => {
    setPage(page + 1);
  };

  return (
    <div>
      <h2>Paginated Query Example</h2>
      <div>
        {isError && <div>{error.message}</div>}

        {isLoading && <div>Loading...</div>}

        {status === "success" && <UserTable users={data} />}
      </div>

      {/* start of pagination buttons */}
      <div>
        <button onClick={prevPage} disabled={page <= 1}>
          Prev
        </button>
        <span>Page: {page}</span>
        <button onClick={nextPage} disabled={data && data.length < pageLimit}>
          Next
        </button>
      </div>
      {/* end of pagination buttons */}
    </div>
  );
}

export default PaginatedQuery;

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

Ниже скриншот PaginatedQueryстраницы.

Ниже скриншот PaginatedQueryстраницы

Бесконечные запросы

До сих пор мы использовали useQueryловушку только для управления извлечением данных из нашего внутреннего API. В этом разделе вы узнаете, как реализовать функцию «бесконечной прокрутки». Пользователи должны будут нажать кнопку » Загрузить еще», чтобы запустить выборку данных.

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

fetch("/api/projects?cursor=0");

К сожалению, наша json-serverсерверная часть этого не делает. Для наших целей мы реализуем обходной путь, используя существующую поддержку разбивки на страницы, чтобы выполнять бесконечные запросы. Давайте посмотрим, как мы определяем нашу fetchUsersфункцию:

const pageLimit = 5;

const fetchUsers = ({ pageParam = 1 }) =>
  axios.get(
    `http://localhost:3004/users?_page=${pageParam}&_limit=${pageLimit}`
  );

Функция fetchUsersаналогична его PaginatedQueryверсии, за исключением того, что мы возвращаем полный Responseобъект вместо разрешенного массива данных. Мы сделали это для того, чтобы иметь доступ к Linkобъекту, указанному в заголовке:

Link: <http://localhost:3004/users?_page=1&_limit=10>; rel="first",
<http://localhost:3004/users?_page=2&_limit=10>; rel="next",
<http://localhost:3004/users?_page=25&_limit=10>; rel="last"

LinkЗаголовок возвращает строку, которая содержит мета — данные о текущей позиции страницы. При использовании Axios мы можем получить доступ к вышеуказанной информации, используя response.headers.link. При использовании Fetch API для выполнения запроса используйте response.headers.get(’Link’)для доступа к нему.

Затем нам нужно преобразовать Linkметаданные в формат, к которому мы можем легко получить доступ в коде. Мы можем выполнить преобразование, используя эту функцию, описанную в статье Джоша Фрэнка :

const parseLinkHeader = (linkHeader) => {
  const linkHeadersArray = linkHeader
    .split(", ")
    .map((header) => header.split("; "));
  const linkHeadersMap = linkHeadersArray.map((header) => {
    const thisHeaderRel = header[1].replace(/"/g, "").replace("rel=", "");
    const thisHeaderUrl = header[0].slice(1, -1);
    return [thisHeaderRel, thisHeaderUrl];
  });
  return Object.fromEntries(linkHeadersMap);
};

Когда мы передаем Linkв функцию строку заголовка, мы получаем следующий объект JavaScript:

{
  first: "http://localhost:3004/users?_page=1&_limit=5",
  next: "http://localhost:3004/users?_page=2&_limit=5",
  last: "http://localhost:3004/users?_page=50&_limit=5"
}

Теперь мы можем извлечь значение для следующей страницы с помощью функции URLSearch. Вам нужно будет указать частичный URL-адрес в том формате, ?_page=2&_limit=5в котором он будет работать. Вот фрагмент кода, из которого мы извлекаем nextPageзначение:

const nextPageUrl = parseLinkHeader(response.headers.link)["next"];
// split URL string
const queryString = nextPageUrl.substring(
  nextPageUrl.indexOf("?"),
  nextPageUrl.length
); // returns '?_page=2&_limit=5'
const urlParams = new URLSearchParams(queryString);
const nextPage = urlParams.get("_page"); // returns 2

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

const {
  data,
  error,
  fetchNextPage,
  hasNextPage,
  isFetchingNextPage,
  status,
} = useInfiniteQuery("infiniteUsers", fetchUsers, {
  getNextPageParam: (lastPage) => {
    // The following code block is specific to json-server api
    const nextPageUrl = parseLinkHeader(lastPage.headers.link)["next"];
    if (nextPageUrl) {
      const queryString = nextPageUrl.substring(
        nextPageUrl.indexOf("?"),
        nextPageUrl.length
      );
      const urlParams = new URLSearchParams(queryString);
      const nextPage = urlParams.get("_page");
      return nextPage;
    } else {
      return undefined;
    }
  },
});

Приведенный выше фрагмент кода выглядит сложным, поэтому позвольте мне пояснить useInfiniteQueryвам синтаксис:

const { ... } = useInfiniteQuery(queryKey, queryFn, {...options})

Мы должны предоставить только три аргумента:

  • Первый аргумент — это queryKey.
  • Второй аргумент — queryFnэто функция обещания, которая выбирает данные с разбивкой на страницы курсора.
  • Третий аргумент — это объект конфигурации JavaScript, в котором вы определяете такие параметры, как staleTimeи cacheTime.

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

{
  getNextPageParam: (lastPage, allPages) => {
    // lastPage: the last page(in our case last `Response` object) fetched by `fetchUsers` function
    // allPages: List of all pages that have already been fetched
    // return int|undefined : return `nextPage` as integer. Return `undefined` when there are no more pages
  };
}

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

  • data: возвращает массив страниц, data.pages[]
  • fetchNextPage: когда эта функция выполняется, она загружает следующую страницу, полагаясь на работу getNextPageParamфункции
  • hasNextPage: возвращает истину, если есть следующая страница
  • isFetchingNextPage: возвращает истину при выборке следующей страницы с помощью fetchNextPage

Ниже приведен фрагмент того, как возвращаемые состояния используются для определения нашей Load moreкнопки:

<button
  onClick={() => fetchNextPage()}
  disabled={!hasNextPage || isFetchingNextPage}
>
  Load More...
</button>

В data.pages[]массиве каждый pageэлемент представляет собой массив, содержащий записи данных. Каждый раз, когда пользователь нажимает кнопку « Загрузить еще» , pageк data.pages[]массиву добавляется новый элемент . Нам нужно определить новую функцию для извлечения записей из этой вложенной структуры. Обратите внимание, что в этом случае каждый pageявляется Responseобъектом Axios , поэтому нам нужно указать page.dataдоступ к каждой записи пользователя.

Ниже приведен фрагмент кода, который мы будем использовать для сопоставления каждого пользователя с <li>тегом:

userList = data.pages.map((page, index) => (
  <React.Fragment key={index}>
    {page.data.map((user) => (
      <li key={user.id}>
        {user.id}. {user.first_name} {user.last_name}
      </li>
    ))}
  </React.Fragment>
));

К настоящему моменту у вас должно быть фундаментальное понимание того, как использовать useInfiniteQueryкрючок. Давайте теперь посмотрим, как все views/InfiniteQuery.jsxвыглядит:

import React from "react";
import { useInfiniteQuery } from "react-query";
import axios from "axios";

function InfiniteQuery() {
  const pageLimit = 5;

  const fetchUsers = ({ pageParam = 1 }) =>
    axios.get(
      `http://localhost:3004/users?_page=${pageParam}&_limit=${pageLimit}`
    );

  const parseLinkHeader = (linkHeader) => {
    const linkHeadersArray = linkHeader
      .split(", ")
      .map((header) => header.split("; "));
    const linkHeadersMap = linkHeadersArray.map((header) => {
      const thisHeaderRel = header[1].replace(/"/g, "").replace("rel=", "");
      const thisHeaderUrl = header[0].slice(1, -1);
      return [thisHeaderRel, thisHeaderUrl];
    });
    return Object.fromEntries(linkHeadersMap);
  };

  const {
    data,
    error,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
    status,
  } = useInfiniteQuery("infiniteUsers", fetchUsers, {
    getNextPageParam: (lastPage) => {
      // The following code block is specific to json-server api
      const nextPageUrl = parseLinkHeader(lastPage.headers.link)["next"];
      if (nextPageUrl) {
        const queryString = nextPageUrl.substring(
          nextPageUrl.indexOf("?"),
          nextPageUrl.length
        );
        const urlParams = new URLSearchParams(queryString);
        const nextPage = urlParams.get("_page");
        return nextPage;
      } else {
        return undefined;
      }
    },
  });

  let userList;

  if (data) {
    userList = data.pages.map((page, index) => (
      <React.Fragment key={index}>
        {page.data.map((user) => (
          <li key={user.id}>
            {user.id}. {user.first_name} {user.last_name}
          </li>
        ))}
      </React.Fragment>
    ));
  }

  return (
    <div>
      <h2>Infinite Query</h2>
      <div>
        {error && <div>An error occurred: {error.message}</div>}

        {isFetchingNextPage && <div>Fetching Next Page...</div>}

        {status === "success" && <ul className="my-8 ml-4">{userList}</ul>}
      </div>
      <div>
        <button
          onClick={() => fetchNextPage()}
          disabled={!hasNextPage || isFetchingNextPage}
        >
          Load More...
        </button>
      </div>
    </div>
  );
}

export default InfiniteQuery;

Надеюсь, к настоящему времени завершенный код должен иметь смысл, поскольку все разделы были объяснены. Ниже приведен снимок экрана со страницей «Пример бесконечного запроса». Я сократил количество db.jsonпользователей до 13, чтобы продемонстрировать результаты ниже:

Я сократил количество db.jsonпользователей до 13

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

Мутации

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

Для useMutationловушки требуется только функция обещания, которая будет отправлять данные во внутренний API. Он вернет следующие состояния:

  • isLoading: возвращает истину во время выполнения асинхронной операции
  • isError: возвращает истину, если произошла ошибка
  • error: возвращает объект ошибки, если он присутствует
  • isSuccess: возвращает истину после того, как мутация стала успешной

Все, что вам нужно сделать, чтобы выполнить собственно мутационное действие, — это выполнить mutation.mutate(data). Вы можете заключить его как функцию и назначить его событию нажатия кнопки.

Ниже приведен снимок views/CreateUser.jsxстраницы. Вы можете увидеть, как каждая переменная состояния использовалась для визуализации различных элементов пользовательского интерфейса.

import { useMutation } from "react-query";
import axios from "axios";
import { Redirect } from "react-router-dom";
import UserForm from "../components/UserForm";

const postUser = async (newUser) =>
  await (await axios.post("http://localhost:3004/users", newUser)).data;

function CreateUser() {
  const mutation = useMutation((newUser) => postUser(newUser));
  const { isLoading, isError, error, isSuccess } = mutation;

  const onSubmit = async (data) => {
    mutation.mutate(data);
  };

  if (isSuccess) {
    return <Redirect to="/" />;
  }

  return (
    <div>
      <h2>New User</h2>

      {isError && <div>An error occurred: {error.message}</div>}

      {isLoading && <div>Loading...</div>}

      <UserForm submitText="Create" submitAction={onSubmit} />
    </div>
  );
}

Мутация для действий обновления и удаления аналогична. Единственное отличие — это функция обещания, которую вы предоставляете, и требуемые аргументы.

Пример обновления мутации:

const mutation = useMutation((updatedUser) =>
  axios.put(`http://localhost:3004/users/${id}`, updatedUser)
);

Пример удаления мутации:

const deleteMutation = useMutation((id) =>
  axios.delete(`http://localhost:3004/users/${id}`)
);

Если ваш код мутации выполняется на странице, где отображаются данные вашего запроса, вы заметите, что после фиксации ничего не изменится. Чтобы вызвать повторную выборку данных после успешной мутации, вам необходимо выполнить queryClient.invalidateQueries()функцию. См. Пример ниже о том, как его назвать:

import { useMutation, useQueryClient } from "react-query";

function UserTable() {
  const deleteMutation = useMutation(
    (id) => axios.delete(`http://localhost:3004/users/${id}`),
    {
      onSuccess: () => {
        queryClient.invalidateQueries();
      },
    }
  );
}

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

Заключение

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

Из этого материала вы узнаете:

  • Зачем бизнесу данные клиентов
  • Варианты клиентских баз
  • Какие минимальные данные обязана содержать клиентская база
  • 12 наиболее эффективных способов получить данные клиентов
  • Где хранить клиентские данные

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

Что такое CRM-маркетинг для бизнеса

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

Зачем бизнесу данные клиентов

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

Онлайн-чат для сайта

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

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

Зачем бизнесу данные клиентов

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

Варианты клиентских баз

На сегодняшний день известно 3 вида клиентских баз.

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

Получение данных от клиента: 12 наиболее эффективных способов

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

Варианты клиентских баз

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

Какие минимальные данные должна содержать клиентская база

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

  • Электронная почта

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

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

  • Физический (почтовый) адрес

    Обычной почтой можно поздравлять вип-клиентов с праздниками, приглашать их на какие-либо мероприятия, оповещать о выгодных акциях. Кроме того, физический адрес позволяет знать местоположение клиентов. А это значит, их можно таргетировать, что дает возможность эффективнее размещать рекламные объявления, в том числе в сети.

  • Увлечения

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

  • Давние предпочтения

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

Как увеличить партнерские продажи

12 наиболее эффективных способов получить данные клиентов

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

1. Подписка на рассылку

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

2. Функция обратного звонка

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

3. Чат-бот

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

Получение данных от клиента: 12 наиболее эффективных способов

4. Проведение опросов через email-рассылки

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

5. Оформление оплаченного заказа

Здесь все предельно просто. Оформляя заказ, продавец просит покупателя оставить свои контактные данные.

6. Вознаграждение за регистрацию

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

7. Интерактивный тест и всевозможные калькуляторы

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

8. Конкурсы

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

9. Социальные сети

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

10. Системы аналитики

Аналитические сервисы типа Яндекс.Метрики и Google Analitics после подключения к сайту позволяют отслеживать поведение посетителей. Станут известными источники трафика, история посещения каждого пользователя и время, проведенное им на сайте, а также другая полезная информация. Имеется возможность автоматически переносить полученные данные в CRM-систему и привязывать их к конкретному клиенту.Системы аналитики

11. Внутренние источники

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

12. Внешние источники

Получение данных от клиента: 12 наиболее эффективных способов

Где хранить клиентские данные

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

Excel

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

Excel

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

Построение клиентской базы с нуля в табличном редакторе — достаточно удобный вариант. Однако он имеет также рад недостатков:

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

Access

В отличие от Excel другой продукт Microsoft, Access, обеспечивает полноценную защиту данных. Усовершенствованные версии пакета позволяют помимо загрузки данных организовывать их в удобной облачной веб-базе. Поскольку оба продукта являются частью MS Office, они имеют схожий интерфейс, поэтому сложности в работе у операторов возникнуть не должно.

Access

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

CRM-система для интернет-магазина

Системы управления взаимоотношениями с клиентами (именно так расшифровывается и переводится на русский язык CRM) предназначены для автоматизации и структурирования взаимодействия продавца с клиентами. При этом:

  • создается полноценная клиентская база для компании любого уровня;
  • облегчается контроль деятельности отдела продаж;
  • обеспечивается возможность вести статистику эффективности различных маркетинговых кампаний;
  • увеличивается до отметки 5-7 % процент удержания клиентов.

CRM-система от Envybox

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

Получение данных от клиента: 12 наиболее эффективных способов

Выбирая подходящую систему, удостоверьтесь в наличии всех требуемых функций, а также в простоте работы с ней для сотрудников. Слишком сложная программа скорее доставит больше неудобств, чем принесет реальную пользу. На выбор сегодня предлагается множество программных продуктов, одни из них более популярны, другие — менее. Большой популярностью у многих предпринимателей пользуется CRM-система от Envybox, отличающаяся простотой, удобством и высокой эффективностью.

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

Российская CRM-система

О сборе данных. Как собирать данные, анализировать их и грабить корованы

Время на прочтение
8 мин

Количество просмотров 20K


В предыдущей статье мы рассматривали вопросы качества данных («О качестве данных и распространенных ошибках при их сборе» на Хабре).
Сегодня я хочу продолжить разговор о качестве данных и обсудить их сбор: как правильно расставить приоритеты при выборе источника, как и какие данные собирать, оценка ценности данных для компании и другое.

Собирайте всё

Вы решили улучшить оформление и оплату товара на сайте?
Отлично, а как проходит процесс формирования корзины покупателем? В какой момент он делает окончательный выбор товаров: до добавления в корзину или перед оплатой покупки?
На каждом сайте может быть по разному, но как ведет себя клиент у вас?
При обладании данными об оформлении заказа их можно проанализировать и определиться с вектором обновления, который будет удобен не только вам, но и пользователям.

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

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

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

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

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

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

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

Скорость
Какой объем данных требуется обрабатывать в единицу времени?
Вспомним недавние выборы президента США — благодаря быстрой обработке сообщений Twitter можно было понимать настроение избирателей в ходе дебатов и корректировать их ход.

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

Приоритеты и выбор источников данных

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

Главная цель работы аналитиков — давать необходимую другим подразделениям информацию качественно и своевременно. Эта информация оказывает прямое влияние на эффективность компании и работу отделов.

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

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

Список параметров для расстановки приоритетов

Высокая

Причина:

Данные нужны немедленно.

Объяснение:

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

Высокая

Причина:

Данные повышают ценность.

Объяснение:

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

Высокая

Причина:

Разным командам требуются одни и те же данные.

Объяснение:

Удовлетворяя потребности нескольких команд в данных вы повышаете ROI.

Высокая

Причина:

Краткосрочные или потоковые данные.

Объяснение:

Некоторые интерфейсы и протоколы дают ограниченное по времени «окно» для сбора данных, следует поторопиться.

Средняя

Причина:

Дополнение для существующего набора данных, которые повышают их качества.

Объяснение:

Новые данные дополняют имеющиеся и улучшают понимание контекста действий.

Средняя

Причина:

Код обработки данных может быть использован повторно.

Объяснение:

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

Средняя

Причина:

Данные легко доступны.

Объяснение:

Если данные ценны, а добыть их просто — вперед.

Средняя

Причина:

Удобный API позволяет собрать данные за прошедшие периоды.

Объяснение:

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

Низкая

Причина:

Аналитики имеют доступ к данным или иные пути их получения.

Объяснение:

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

Низкая

Причина:

Низкое качество данных.

Объяснение:

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

Низкая

Причина:

Необходимо извлечение из веб-страниц.

Объяснение:

Обработка таких данных может быть достаточно сложной и требовать чрезмерных усилий.

Низкая

Причина:

Низкая вероятность использования данных.

Объяснение:

Данные, которые хорошо бы иметь, но если их нет, то и ладно.
Зато, обладая этими данными, можно грабить корованы!

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

Взаимосвязь данных

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

Для примера рассмотрим Диану и ее заказ. Недавно она заказала комплект садовой мебели, сопоставив ее заказ с данными аналитики, мы видим, что она провела на сайте 30 минут и просмотрела 20 разных наборов. Это значит, что она выбирала мебель уже на сайте, не зная заранее, что будет заказывать.
Смотрим откуда она пришла — поисковая выдача.

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

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

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

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

Сбор и покупка данных

Сегодня существует множество способов сбора данных, один из самых распространенных — API. Но кроме того как собрать данные, их нужно обновлять, и тут все уже зависит от объема.

Небольшие объемы данных (до 100 тысяч строк) целесообразнее заменять свежими, а вот с крупными массивами уже актуально частичное обновление: добавление новых и удаление устаревших значений.

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

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

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

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

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

Качество
Данные чисты, им можно доверять?

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

Выборка
Есть возможность получить выборку для оценки качества данных до приобретения?

Обновления
Какой срок жизни данных, как быстро они устаревают, будут ли они обновляться и как часто?

Надежность
Какие ограничения у интерфейсов получения данных, какие еще ограничения могут накладываться на вас?

Безопасность
Если данные важны, то будут ли они зашифрованы и насколько надежными протоколами? Также не стоит забывать о безопасности при их передаче.

Условия использования
Лицензирование или иные ограничения. Что может не позволить вам воспользоваться данными в полном объеме?

Формат
Насколько вам удобно работать с форматом приобретаемых данных? Есть ли возможность их интеграции в вашу систему?

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

Объем
Если данных много, вы сможете обеспечить их хранение и обработку? Ценные данные не всегда будут объемные, как и наоборот.

Степень детализации
Эти данные подходят для уровня необходимой вам аналитики?

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

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

Источник информации

Автор: Карл Андерсон
Аналитическая культура. От сбора данных до бизнес-результатов
Creating a Data-Driven Organization
ISBN: 978-5-00100-781-4
Издательство: Манн, Иванов и Фербер

Опубликованы внутренние инструкции ФБР по получению данных от операторов связи

11:20 / 27 октября, 2021
2021-10-27T11:20:22+03:00

Датированная 2019 годом презентация на 139 страницах была создана Командой аналитики сотовой связи ФБР.

Журналистам издания Motherboard удалось ознакомиться с документами ФБР, в которых необычно подробно описывается, как бюро и другие правоохранительные органы получают от телекоммуникационных компаний информацию о местоположении мобильных устройств.

Большая часть представленной в документах информации уже и так известна широкой общественности, например о том, как правоохранительные органы могут получать данные абонентов операторов связи с помощью специальных судебных ордеров. Однако документы также проливают свет на то, какую именно информацию собирает каждый оператор и как долго хранит определенные типы данных. Более того, в них представлены скриншоты инструментов, предоставляемых ФБР правоохранительным органам по всей территории США для анализа данных с базовых станций сотовой связи.

Документы были предоставлены изданию исполнительным директором некоммерческой организации Property of the People Райаном Шапиро (Ryan Shapiro), который получил их по запросу информации из открытых источников. Датированная 2019 годом презентация на 139 страницах была создана Командой аналитики сотовой связи ФБР (Cellular Analysis Survey Team, CAST).

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

«При необходимости CAST применяет стандартное промышленное оборудование для определения истинного географического покрытия сектора сотовой связи», — говорится в презентации.

CAST бесплатно предоставляет правоохранительным органам собственный программный инструмент для визуализации данных телефонов под названием CASTViz.

«CASTViz может быстро и подробно записывать данные о звонках и данные с базовых станций для генерации лидов и расследований», — говорится в презентации.

В презентации даже содержатся скриншоты инструкций по использованию CASTViz.

В документе объясняется, как подаются запросы на получение данных о местоположении одноразовых телефонов от виртуальных операторов связи (операторов, использующих существующую инфраструктуру другого оператора, но продающих услуги под собственной маркой). Также описывается, как получать информацию из системы автомобиля General Motors OnStar, и представлена стоимость некоторых данных.

Что касается периода, в течении которого операторы связи хранят данные телефонных звонков, то для AT&T это 7 лет, T-Mobile — 2 года, а Verizon — 1 год. Данные web-браузинга AT&T хранит 1 год.


Цифровой мир не прощает ошибок — подписывайтесь на наш канал и узнавайте, как избежать неприятностей и защитить свои данные!


Понравилась статья? Поделить с друзьями:
  • Как отправить бандероль почтой россии инструкция цена
  • Металлургический инвестиционный банк руководство
  • Охрана труда на предприятии с нуля пошаговые инструкции 2022
  • Руководство по ремонту автомобиля toyota trueno
  • Руководство по эксплуатации ивеко стралис курсор 10