Advertisement
RotLenin

Untitled

May 25th, 2024 (edited)
677
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Мы ожидаем, что Вы исправите синтаксические ошибки, сделаете перехват возможных исключений и улучшите читаемость кода.
  2. // А так же, напишите кастомный хук useThrottle и используете его там где это нужно. - не пригодилось, реализация через pending
  3. // Желательно использование React.memo и React.useCallback там где это имеет смысл.
  4. // Будет большим плюсом, если Вы сможете закэшировать получение случайного пользователя.
  5. // Укажите правильные типы.
  6. // По возможности пришлите Ваш вариант в https://codesandbox.io
  7.  
  8. import React, { useState, useCallback } from "react";
  9. import type {MouseEvent} from "react";
  10.  
  11. const URL = "https://jsonplaceholder.typicode.com/users";
  12.  
  13. function getRandomInt(min : number, max : number) {
  14.     return Math.floor(Math.random() * (max - min + 1)) + min;
  15. }
  16.  
  17. interface Company {
  18.   bs: string;
  19.   catchPhrase: string;
  20.   name: string;
  21. }
  22.  
  23. interface User {
  24.   id: number;
  25.   email: string;
  26.   name: string;
  27.   phone: string;
  28.   username: string;
  29.   website: string;
  30.   company: Company;
  31.   address: string
  32. }
  33.  
  34. enum FetchStatus {
  35.     pending = 'pending',
  36.     success = 'success',
  37.     error = 'error',
  38.     null = 'null' // первый запрос
  39. }
  40.  
  41. interface WithPendingProps {
  42.     status : FetchStatus,
  43. }
  44. // Если подход с ожиданием ответа будет применятся по проекту - будет неплохо вынести этот функционал отдельно
  45. function withPending<T>(WrappedComponent : any) {
  46.     return function (props : T & WithPendingProps) {
  47.         const {status} = props;
  48.         if(status === FetchStatus.pending) {
  49.             return <div>
  50.                 <p>Ожидайте идет получение данных</p>
  51.             </div>
  52.         }
  53.  
  54.         if(status === FetchStatus.error) {
  55.             return <div>
  56.                 <p>Одна ошибка и ты ошибся !</p>
  57.             </div>
  58.         }
  59.  
  60.         return <>
  61.             <WrappedComponent {...props} />
  62.         </>
  63.     }
  64. }
  65.  
  66. interface IUserInfoProps {
  67.     user: User | null;
  68. }
  69. // Нет смысла мемоизировать - часто меняется
  70. function UserInfo({ user }: IUserInfoProps) {
  71.     if(user === null) {
  72.         return <></>
  73.     }
  74.  
  75.     return <>
  76.         <td>{user.name}</td>
  77.         <td>{user.phone}</td>
  78.     </>
  79. }
  80.  
  81. // Инициализируем компонент и интерфейс под оберткой
  82. const UserInfoWithStatus = withPending<IUserInfoProps>(UserInfo)
  83.  
  84. const App = () => {
  85.     // Текущий пользователь, по умолчанию пусто
  86.     const [item, setItem] = useState<User | null>(null);
  87.     // Можно усложнить и добавить время жизни Кэша
  88.     const [cache] = useState<Map<number, User>>(new Map());
  89.     // Текущее состояние запроса, запрещаем отправлять еще 1 пока первый не вернулся текущий запрос
  90.     const [isPending, setIsPending] = useState<FetchStatus>(FetchStatus.null);
  91.  
  92.     // Запрос на получение нового пользователя, с проверкой Кэша
  93.     const receiveRandomUser = useCallback(async () => {
  94.       // Есть пользователи с 1-10. Специально сделаем шанс ошибки - чтобы проверить работу ERROR статуса
  95.       // TODO: Можно предусмотреть чтобы не совпадал с последним, но не критично
  96.       const id = getRandomInt(0, 11);
  97.       // Проверяем есть ли данные в Cache
  98.       if(cache.has(id)) {
  99.           setItem(cache.get(id) as User);
  100.           setIsPending(FetchStatus.success);
  101.           return;
  102.       }
  103.  
  104.       // Получаем пользователя
  105.       setIsPending(FetchStatus.pending);
  106.       fetch(`${URL}/${id}`)
  107.           .then(res => {
  108.               if(!res.ok) {
  109.                   throw new Error('bad request');
  110.               }
  111.               return res.json() as Promise<User>
  112.           })
  113.           .then(_user => {
  114.               setIsPending(FetchStatus.success);
  115.               cache.set(id, _user)
  116.               setItem(_user);
  117.           })
  118.           .catch(err => {
  119.               console.log(`catch`);
  120.               // Возможно стоит получать и отображать ошибку в теле компонента, уточнить у ПМ-а =)
  121.               setIsPending(FetchStatus.error)
  122.           })
  123.     }, [cache, setItem]);
  124.  
  125.     const handleButtonClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
  126.         event.stopPropagation();
  127.         receiveRandomUser();
  128.     }, [receiveRandomUser]);
  129.  
  130.     // Throttle не нужен из-за наличия статуса запроса
  131.     // Таблица вынесена отдельна, чтобы меньше HTML элементов перерисовывалось
  132.     // Честно говоря абсолютно не уверен что добился чего хотел, но для такого компонента задуманный рефактор избыточен
  133.     return (
  134.       <div>
  135.         <h1>Get a random user</h1>
  136.         <button
  137.             type="button"
  138.             onClick={handleButtonClick}
  139.             disabled={isPending === FetchStatus.pending}
  140.         >
  141.             get random user
  142.         </button>
  143.           <table>
  144.               <thead>
  145.               <tr>
  146.                   <th>Username</th>
  147.                   <th>Phone number</th>
  148.               </tr>
  149.               </thead>
  150.               <tbody>
  151.               <tr>
  152.                 <UserInfoWithStatus user={item} status={isPending}/>
  153.               </tr>
  154.               </tbody>
  155.           </table>
  156.       </div>
  157.   );
  158. }
  159.  
  160. export default App;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement