Создание пользовательского хука для получения асинхронных данных: useAsync Hook с кешем
17 ноября 2022 г.Хорошей практикой является показать пользователю, что приложение загружает данные. Для этого отображается индикатор загрузки и скрывается содержимое до тех пор, пока данные не будут готовы. Большинство из нас будет поддерживать состояние в компоненте, который отслеживает, готовы данные или нет, и это повторяется в каждом компоненте, который вызывает API.
Рассмотрите следующий пример:
Компонент Todos
import React, { useState, useEffect } from "react";
const Todos = () => {
const [todos, setTodos] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null); useEffect(() => {
const init = async () => {
try {
setLoading(true);
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos"
);
const data = await response.json();
setTodos(data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
init();
}, []); if (loading) return <div>Loading...</div>;
if (error) return <div>Error</div>;
return (
<div>
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</div>
);
};
Детали TODO
const Todo = ({ id }) => {
const [todo, setTodo] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null); useEffect(() => {
const init = async () => {
try {
setLoading(true);
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${id}`
);
const data = await response.json();
setTodo(data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
init();
}, [id]); if (loading) return <div>Loading 2...</div>;
if (error) return <div>Error 2</div>;
return (
<div>
<h1>{todo.title}</h1>
</div>
);
};
Как мы видим, в коде происходят три основные вещи:
- Во-первых, во время выборки мы показываем индикатор загрузки.
- Во-вторых, мы обрабатываем ошибку, если она есть.
- В-третьих, мы устанавливаем состояние задачи на данные, полученные от API.
Примечание. Логика получения данных одинакова в обоих компонентах. Мы можем создать собственный хук, который будет использоваться для обработки всех асинхронных выборок данных и обновления состояния.
Пользовательский хук (useAsync)
Перехватчики React – это набор функций, которые можно использовать для создания более гибкого компонента, чем традиционный жизненный цикл компонента.
Мы можем создать собственный хук, который будет использоваться для обработки всех асинхронных выборок данных и обновления состояния.
перехватчик useAsync
import React, { useState, useEffect } from "react";const useAsync = (defaultData) => {
const [data, setData] = useState({
data: defaultData ?? null,
error: null,
loading: false,
}); const run = async (asyncFn) => {
try {
setData({ data: null, error: null, loading: true });
const response = await asyncFn();
const result = { data: response, error: null, loading: false };
setData(result);
return result;
} catch (error) {
const result = { data: null, error, loading: false };
setData(result);
return result;
}
}; return {
...data,
run,
};
};
Компонент Todos
import React, { useState, useEffect } from "react";
import { useAsync } from "./hooks";
const Todos = () => {
const { data, loading, error, run } = useAsync([]); useEffect(() => {
run(() => fetch("https://jsonplaceholder.typicode.com/todos").then((res) => res.json()));
}, []); // Same as above
return ...
};
Детали TODO
import React, { useState, useEffect } from "react";
import { useAsync } from "./hooks";
const Todo = ({ id }) => {
const { data, loading, error, run } = useAsync(null); useEffect(() => {
run(() => fetch(`https://jsonplaceholder.typicode.com/todos/${id}`).then((res) => res.json()));
}, [id]); // Same as above
return ...
};
ПРИМЕЧАНИЕ. Мы сократили объем кода, который необходимо написать, с помощью пользовательского хука. Код также легче читать и поддерживать.
Давайте добавим больше функциональности в наш собственный хук
- Добавьте кэширование в пользовательский хук, чтобы предотвратить вызовы API, если данные уже присутствуют в состоянии.
import { useState, useCallback } from "react";const cache = new Map();
const defaultOptions = {
cacheKey: "",
refetch: false,
};export const useAsync = (defaultData?: any) => {
const [data, setData] = useState({
data: defaultData ?? null,
error: null,
loading: false,
}); const run = useCallback(async (asyncFn, options = {}) => {
try {
// Merge the default options with the options passed in
const { cacheKey, refetch } = { ...defaultOptions, ...options }; const result = { data: null, error: null, loading: false }; // If we have a cache key and not requesting a new data, then return the cached data
if (!refetch && cacheKey && cache.has(cacheKey)) {
const res = cache.get(cacheKey);
result.data = res;
} else {
setData({ ...result, loading: true });
const res = await asyncFn();
result.data = res;
cacheKey && cache.set(cacheKey, res);
}
setData(result);
return result;
} catch (error) {
const result = { data: null, error: error, loading: false };
setData(result);
return result;
}
}, []); return {
...data,
run,
};
};
Детали TODO
import React, { useState, useEffect } from "react";
import { useAsync } from "./hooks";
const Todo = ({ id }) => {
const { data, loading, error, run } = useAsync(null); useEffect(() => {
run(() => fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then((res) => res.json()),
{cacheKey: `todo-${id}`});
}, [id]); // Same as above
return ...
};
Варианты:
- cacheKey: ключ, который мы будем использовать для хранения данных в кеше.
- обновить: если мы хотим повторно получить данные из API. Это полезно, когда мы хотим обновить данные в кеше.
ПРИМЕЧАНИЕ. Кэш доступен глобально, поэтому мы можем использовать его в других компонентах. Если мы используем useAsync в нескольких компонентах с одним и тем же cacheKey, то данные кеша будут общими для всех компонентов. Это полезно, когда мы хотим избежать ненужных вызовов API, если данные уже присутствуют в кеше.
React Query и SWR — две популярные библиотеки, которые можно использовать для обработки всех асинхронных выборок данных.
Живой пример, здесь h2>
Изображения для ведущих источник.
Также опубликовано здесь
Спасибо, что читаете 😊
Есть вопросы или дополнения? пожалуйста, оставьте комментарий.
Оригинал