How To Create A Profile Iterator Custom Hook With React and TypeScript

Final code

useProfileIterator.ts

import { useEffect, useState } from "react";
import axios from "axios";

export type User = {
    name: string,
    thumbnail: string,
    age: number,
    phone: string,
    email: string
}

type Function = () => void;

const useProfileIterator = (url: string) : [User, boolean, Function, Function] => {

    const [users, setUsers] = useState<User[]>([]);
    const [index, setIndex] = useState(0);
    const [loading, setLoading] = useState(false);

    const fetchUser = async () => {

        try {
            setLoading(true);

            const {data} = await axios.get(url);

            if(data.results && data.results[0]){
                setUsers(prevUsers => {
                    const user = data.results[0];

                    return [
                        ...prevUsers,
                        {
                            name: `${user.name.title} ${user.name.first} ${user.name.last}`,
                            age: user.dob.age,
                            phone: user.phone,
                            email: user.email,
                            thumbnail: user.picture.medium
                        }
                    ]
                });

                return true;
            }

        } catch (error) {
            console.log(error);
            
        }
        finally{
            setLoading(false);
        }
    }

    const previous = () => {
        if(index > 0) setIndex(prev => prev - 1);
    }

    const next = () => {
        if(index < users.length - 1){
            setIndex(prev => prev + 1);
            return;
        }

        fetchUser().then(res => setIndex(prev => prev + 1));
    }

    useEffect(() => {
        fetchUser();
    }, []);

    return [users[index], loading, previous, next];
}


export default useProfileIterator;

 

Users.tsx


import React from 'react'
import useProfileIterator, { User } from '../hooks/useProfileIterator'
import './Users.css'

const Users = () => {

  const [user, loading, previous, next] = useProfileIterator('https://randomuser.me/api');
  
  return (
    <div className='UserContainer'>
        {
            loading ? <Loader /> 
            : <Card user={user} />
        }
        <div className='controls'>
            <button className='previous' onClick={previous}>Previous</button>
            <button className='next' onClick={next}>Next</button>
        </div>
    </div>
  )
}

type UserProps = {
    user: User
}

const Card = ({user} : UserProps) => (
    <div className='Card'>
        <div className='topBG'></div>
        <div className='content'>

            <div className='imgContainerBG'>
                <div className='imgContainer'>
                    <img src={user?.thumbnail} alt={user?.name} />
                </div>
            </div>

            <div className='infos'>
                <span className='fullName'>{user?.name}</span>
                <span className='age'>{user?.age} years</span>
            </div>

            <div className='phone'>{user?.phone}</div>
            <div className='email'>{user?.email}</div>
        </div>
    </div>
)

const Loader = () => (
    <div className='Card Loader'>
        <div className='topBG'></div>
        <div className='content'>

            <div className='imgContainerBG'>
                <div className='imgContainer value'>
                </div>
            </div>

            <div className='infos'>
                <span className='fullName value'></span>
                <span className='age value'></span>
            </div>

            <div className='phone value'></div>
            <div className='email value'></div>
        </div>
    </div>
)

export default Users

Users.css


body{
    background-color: #d6e5fe;
}

.UserContainer{
    max-width: 300px;
    margin: auto;
}

.Card{
    height: 270px;
    background-color: #fff;
    border-radius: 15px;
    margin-bottom: 15px;
    overflow: hidden;
    box-shadow: 0 2px 5px 1px rgb(64 60 67 / 16%);
    display: flex;
    flex-direction: column;
}

.Card .topBG{
    height: 80px;
    background-color: #514bb6;
}

.Card .content{
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: 15px;
    padding-top: 60px;
    align-items: center;
    position: relative;
}

.Card .content .imgContainerBG{
    position: absolute;
    width: 80px;
    height: 80px;
    border-radius: 50%;
    background-color: #fff;
    padding: 5px;
    top: 0;
    left: 50%;
    transform: translate(-50%, -50%);
}

.Card .content .imgContainer{
    width: 100%;
    height: 100%;
    border-radius: 50%;
    overflow: hidden;
}

.Card .content .imgContainer img{
    width: 100%;
}

.Card .content .infos{
    display: flex;
    justify-content: space-between;
    width: 100%;
}

.Card .content .infos .fullName{
    font-weight: bold;
    max-width: 180px;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}

.Card .content .infos .age{
    color: gray;
    max-width: 70px;
}

.controls{
    background-color: #fff;
    border-radius: 15px;
    box-shadow: 0 2px 5px 1px rgb(64 60 67 / 16%);
    display: flex;
    justify-content: space-between;
    padding: 15px;
}

.controls button{
    background-color: #fff;
    cursor: pointer;
    padding: 5px 20px;
    border-radius: 15px;
}

.controls .next{
    color: #514bb6;
    border: 2px solid #514bb6;
}

.controls .next:hover{
    background-color: #514bb6;
    color: #fff;
}

.controls .previous{
    color: #ffa800;
    border: 2px solid #ffa800;
}

.controls .previous:hover{
    background-color: #ffa800;
    color: #fff;
}

.Loader .value{
    animation-duration: 2.2s;
    animation-fill-mode: forwards;
    animation-iteration-count: infinite;
    animation-name: shimmer;
    animation-timing-function: linear;
    background: linear-gradient(to right, #F6F6F6 8%, #F0F0F0 18%, #F6F6F6 33%);
    background-size: 1200px 100%;
    height: 12px;
    width: 80%;
    border-radius: 8px;
  }
  
  @keyframes shimmer {
    0% {
      background-position: -1200px 0;
    }
    100% {
      background-position: 1200px 0;
    }
  }

Cookies!

We use cookies to ensure that we give you the best experience on our website. If you continue to use this site we will assume that you are happy with it.