️ UserCard: React.memo + <a> instead of <div>

This commit is contained in:
divlo 2020-08-03 13:18:58 +02:00
parent 337868cf5f
commit dc962c9120
5 changed files with 152 additions and 119 deletions

View File

@ -33,20 +33,20 @@ const FunctionCard = memo(forwardRef((props, ref) => {
} }
> >
{/* FunctionCard a une hauteur pendant chargement */} {/* FunctionCard a une hauteur pendant chargement */}
<a ref={ref} style={isLoading ? { height: '360px', justifyContent: 'center' } : null} className='FunctionCard col-sm-24 col-md-10 col-xl-7'> <a ref={ref} style={isLoading ? { height: '360px', justifyContent: 'center' } : null} className='FunctionCard col-sm-24 col-md-10 col-xl-7'>
{isLoading && <Loader width='125px' height='125px' />} {isLoading && <Loader width='125px' height='125px' />}
<div className={`FunctionCard__container ${isLoading ? 'd-none' : ''}`}> <div className={`FunctionCard__container ${isLoading ? 'd-none' : ''}`}>
<div className='FunctionCard__top'> <div className='FunctionCard__top'>
<img onLoad={handleLoad} onError={handleError} className='FunctionCard__image' alt={props.title} src={API_URL + props.image} /> <img onLoad={handleLoad} onError={handleError} className='FunctionCard__image' alt={props.title} src={API_URL + props.image} />
<h2 className='FunctionCard__title'>{props.title}</h2> <h2 className='FunctionCard__title'>{props.title}</h2>
<p className='FunctionCard__description text-center'>{props.description}</p> <p className='FunctionCard__description text-center'>{props.description}</p>
</div>
<div className='FunctionCard__info'>
<p className='FunctionCard__category' style={{ backgroundColor: props.categorie.color }}>{props.categorie.name}</p>
<p className='FunctionCard__publication-date'>{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}</p>
</div>
</div> </div>
<div className='FunctionCard__info'>
<p className='FunctionCard__category' style={{ backgroundColor: props.categorie.color }}>{props.categorie.name}</p>
<p className='FunctionCard__publication-date'>{date.format(new Date(props.createdAt), 'DD/MM/YYYY', true)}</p>
</div>
</div>
</a> </a>
</Link> </Link>
) )

View File

@ -0,0 +1,47 @@
.UserCard {
display: flex;
align-items: center;
position: relative;
flex-direction: column;
word-wrap: break-word;
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, 0.25);
border: 1px solid black;
border-radius: 1rem;
margin: 0 0 50px 0;
cursor: pointer;
transition: all 0.3s;
padding: 10px;
}
.UserCard__container {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
color: var(--text-color);
text-decoration: none !important;
}
.UserCard:hover {
transform: translateY(-7px);
}
/* col-md */
@media (min-width: 768px) {
.UserCard {
margin: 0 30px 50px 30px;
}
}
/* col-xl */
@media (min-width: 1200px) {
.UserCard {
margin: 0 20px 50px 20px;
}
}
.UserCard__logo {
width: 150px;
height: 150px;
border-radius: 50%;
object-fit: cover;
}
.UserCard__name {
margin: 30px 0 10px 0;
}

View File

@ -0,0 +1,25 @@
import Link from 'next/link'
import { forwardRef, memo } from 'react'
import { API_URL } from '../../utils/config/config'
import './UserCard.css'
const UserCard = memo(
forwardRef((props, ref) => {
return (
<div className='UserCard col-sm-24 col-md-10 col-xl-7' ref={ref}>
<Link href='/users/[name]' as={`/users/${props.name}`}>
<a className='UserCard__container'>
<img
className='UserCard__logo'
src={API_URL + props.logo}
alt={props.name}
/>
<h2 className='UserCard__name'>{props.name}</h2>
</a>
</Link>
</div>
)
})
)
export default UserCard

View File

@ -1,74 +1,95 @@
import { useState, useEffect, useRef, useCallback } from 'react' import { useState, useEffect, useRef, useCallback } from 'react'
import Link from 'next/link'
import HeadTag from '../../components/HeadTag' import HeadTag from '../../components/HeadTag'
import Loader from '../../components/Loader' import Loader from '../../components/Loader'
import UserCard from '../../components/UserCard/UserCard'
import api from '../../utils/api' import api from '../../utils/api'
import '../../public/css/pages/users.css' import '../../public/css/pages/users.css'
import { API_URL } from '../../utils/config/config'
const Users = () => { const Users = () => {
let pageUsers = 1 let pageUsers = 1
const [inputSearch, setInputSearch] = useState('') const [inputSearch, setInputSearch] = useState('')
const [usersData, setUsersData] = useState({ totalItems: 0, hasMore: true, rows: [] }) const [usersData, setUsersData] = useState({
totalItems: 0,
hasMore: true,
rows: []
})
const [isLoadingUsers, setLoadingUsers] = useState(true) const [isLoadingUsers, setLoadingUsers] = useState(true)
// Récupère les users si la recherche change // Récupère les users si la recherche change
useEffect(() => { useEffect(() => {
pageUsers = 1 pageUsers = 1
getUsersData().then((data) => setUsersData(data)) getUsersData().then(data => setUsersData(data))
}, [inputSearch]) }, [inputSearch])
const getUsersData = async () => { const getUsersData = async () => {
setLoadingUsers(true) setLoadingUsers(true)
const { data } = await api.get(`/users?page=${pageUsers}&limit=15&search=${inputSearch}`) const { data } = await api.get(
`/users?page=${pageUsers}&limit=15&search=${inputSearch}`
)
setLoadingUsers(false) setLoadingUsers(false)
return data return data
} }
const handleSearchChange = (event) => { const handleSearchChange = event => {
setInputSearch(event.target.value) setInputSearch(event.target.value)
} }
// Permet la pagination au scroll // Permet la pagination au scroll
const observer = useRef() const observer = useRef()
const lastUserCardRef = useCallback((node) => { const lastUserCardRef = useCallback(
if (isLoadingUsers) return node => {
if (observer.current) observer.current.disconnect() if (isLoadingUsers) return
observer.current = new window.IntersectionObserver((entries) => { if (observer.current) observer.current.disconnect()
if (entries[0].isIntersecting && usersData.hasMore) { observer.current = new window.IntersectionObserver(
pageUsers += 1 entries => {
getUsersData().then((data) => { if (entries[0].isIntersecting && usersData.hasMore) {
setUsersData((oldData) => { pageUsers += 1
return { getUsersData().then(data => {
totalItems: data.totalItems, setUsersData(oldData => {
hasMore: data.hasMore, return {
rows: [...oldData.rows, ...data.rows] totalItems: data.totalItems,
} hasMore: data.hasMore,
}) rows: [...oldData.rows, ...data.rows]
}) }
} })
}, { threshold: 1 }) })
if (node) observer.current.observe(node) }
}, [isLoadingUsers, usersData.hasMore]) },
{ threshold: 1 }
)
if (node) observer.current.observe(node)
},
[isLoadingUsers, usersData.hasMore]
)
return ( return (
<> <>
<HeadTag <HeadTag title='Utilisateurs' description='Liste des utilisateurs.' />
title='Utilisateurs'
description='Liste des utilisateurs.'
/>
<div className='container text-center'> <div className='container text-center'>
<div className='row justify-content-center'> <div className='row justify-content-center'>
<div className='col-24'> <div className='col-24'>
<h1 style={{ marginBottom: 0, paddingTop: '20px' }}>Utilisateurs</h1> <h1 style={{ marginBottom: 0, paddingTop: '20px' }}>
<p style={{ marginTop: '5px' }}>La liste des utilisateurs - Total de {usersData.totalItems} utilisateurs :</p> Utilisateurs
</h1>
<p style={{ marginTop: '5px' }}>
La liste des utilisateurs - Total de {usersData.totalItems}{' '}
utilisateurs :
</p>
</div> </div>
</div> </div>
<div className='Users__search-container row justify-content-center'> <div className='Users__search-container row justify-content-center'>
<input value={inputSearch} onChange={handleSearchChange} type='search' className='Users__form-control Users__search-input' name='search' id='search' placeholder='🔎 Rechercher...' /> <input
value={inputSearch}
onChange={handleSearchChange}
type='search'
className='Users__form-control Users__search-input'
name='search'
id='search'
placeholder='🔎 Rechercher...'
/>
</div> </div>
<div className='row justify-content-center'> <div className='row justify-content-center'>
@ -76,25 +97,11 @@ const Users = () => {
// Si c'est le dernier élément // Si c'est le dernier élément
if (usersData.rows.length === index + 1) { if (usersData.rows.length === index + 1) {
return ( return (
<div ref={lastUserCardRef} key={user.id} className='UserCard col-sm-24 col-md-10 col-xl-7'> <UserCard key={index} {...user} ref={lastUserCardRef} />
<Link href='/users/[name]' as={`/users/${user.name}`}>
<div className='UserCard__container'>
<img className='UserCard__logo' src={API_URL + user.logo} alt={user.name} />
<h2 className='UserCard__name'>{user.name}</h2>
</div>
</Link>
</div>
) )
} }
return ( return (
<div key={user.id} className='UserCard col-sm-24 col-md-10 col-xl-7'> <UserCard key={index} {...user} />
<Link href='/users/[name]' as={`/users/${user.name}`}>
<div className='UserCard__container'>
<img className='UserCard__logo' src={API_URL + user.logo} alt={user.name} />
<h2 className='UserCard__name'>{user.name}</h2>
</div>
</Link>
</div>
) )
})} })}
</div> </div>

View File

@ -1,65 +1,19 @@
.Users__form-control { .Users__form-control {
display: block; display: block;
height: calc(1.5em + .75rem + 2px); height: calc(1.5em + 0.75rem + 2px);
padding: .375rem .75rem; padding: 0.375rem 0.75rem;
font-size: 1rem; font-size: 1rem;
font-weight: 400; font-weight: 400;
line-height: 1.5; line-height: 1.5;
color: #495057; color: #495057;
background-color: #fff; background-color: #fff;
background-clip: padding-box; background-clip: padding-box;
border: 1px solid #ced4da; border: 1px solid #ced4da;
border-radius: .5em; border-radius: 0.5em;
} }
.Users__search-container { .Users__search-container {
margin-bottom: 50px; margin-bottom: 50px;
} }
.Users__search-input { .Users__search-input {
width: 50%; width: 50%;
} }
.UserCard {
display: flex;
align-items: center;
position: relative;
flex-direction: column;
word-wrap: break-word;
box-shadow: 0px 0px 6px 6px rgba(0, 0, 0, .25);
border: 1px solid black;
border-radius: 1rem;
margin: 0 0 50px 0;
cursor: pointer;
transition: all .3s;
padding: 10px;
}
.UserCard__container {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.UserCard:hover {
transform: translateY(-7px);
}
/* col-md */
@media (min-width: 768px) {
.UserCard {
margin: 0 30px 50px 30px;
}
}
/* col-xl */
@media (min-width: 1200px) {
.UserCard {
margin: 0 20px 50px 20px;
}
}
.UserCard__logo {
width: 150px;
height: 150px;
border-radius: 50%;
object-fit: cover;
}
.UserCard__name {
margin: 30px 0 10px 0;
}