Передача пропсів до компонента

Компоненти React використовують пропси для спілкування один з одним. Кожен батьківський компонент може передавати певну інформацію своїм дочірнім компонентам, надаючи їм пропси. Пропси можуть нагадувати вам атрибути HTML, але ви можете передавати через них будь-які значення JavaScript, включаючи об'єкти, масиви та функції.

  • Як передати пропси до компонента
  • Як прочитати пропси з компонента
  • Як вказати значення за замовчуванням для пропсів
  • Як передати деякий JSX до компонента
  • Як змінюються реквізити з часом

Знайомі пропси

Пропси - це інформація, яку ви передаєте в JSX-тег. Наприклад, className, src, alt, width і height є деякими з пропсів, які ви можете передати до тегу <img>:

function Avatar() {
  return (
    <img
      className="avatar"
      src="https://i.imgur.com/1bX5QH6.jpg"
      alt="Lin Lanying"
      width={100}
      height={100}
    />
  );
}

export default function Profile() {
  return (
    <Avatar />
  );
}
body { min-height: 120px; }
.avatar { margin: 20px; border-radius: 50%; }

Пропси, які ви можете передати тегу <img>, визначені наперед (ReactDOM відповідає стандарту HTML). Але ви можете передавати будь-які пропси до ваших власних компонентів, таких як <Avatar>, щоб кастомізувати їх. Ось як це зробити!

Передача пропсів до компонента

У цьому коді компонент Profile не передає жодних пропсів своєму дочірньому компоненту, Avatar:

export default function Profile() {
  return (
    <Avatar />
  );
}

Ви можете надати Avatar деякі пропси у два кроки.

Крок 1: Передача пропсів дочірньому компоненту

Спочатку передамо деякі пропси до Avatar. Наприклад, давайте передамо два пропси: person (об'єкт), і size (число):

export default function Profile() {
  return (
    <Avatar
      person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
      size={100}
    />
  );
}

Якщо подвійні фігурні дужки після person= вас бентежать, пам'ятайте, що це просто об'єкт всередині фігурних дужок JSX.

Тепер ви можете прочитати ці пропси всередині компонента Avatar.

Крок 2: Читання пропсів всередині дочірнього компонента

Ви можете прочитати ці пропси, перерахувавши їхні назви person, size через кому всередині ({ та }) безпосередньо після function Avatar. Це дозволяє використовувати їх всередині коду Avatar, як змінну.

function Avatar({ person, size }) {
  // person and size are available here
}

Додайте трохи логіки до Avatar, яка використовує пропси person та size для рендерингу, і все буде готово.

Тепер ви можете налаштувати Avatar на рендеринг різними способами з різними пропсами. Спробуйте змінити значення!

import { getImageUrl } from './utils.js';

function Avatar({ person, size }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <div>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi', 
          imageId: 'YfeOqp2'
        }}
      />
      <Avatar
        size={80}
        person={{
          name: 'Aklilu Lemma', 
          imageId: 'OKS67lh'
        }}
      />
      <Avatar
        size={50}
        person={{ 
          name: 'Lin Lanying',
          imageId: '1bX5QH6'
        }}
      />
    </div>
  );
}
export function getImageUrl(person, size = 's') {
  return (
    'https://i.imgur.com/' +
    person.imageId +
    size +
    '.jpg'
  );
}
body { min-height: 120px; }
.avatar { margin: 10px; border-radius: 50%; }

Пропси дозволяють вам думати про батьківські та дочірні компоненти незалежно. Наприклад, ви можете змінити пропси person або size всередині Profile без необхідності думати про те, як їх використовує Avatar. Аналогічно, ви можете змінити спосіб використання цих пропсів у Avatar, не звертаючи уваги на Profile.

Ви можете подумати про пропси на кшталт "ручок", які ви можете регулювати. Вони виконують ту ж роль, що й аргументи для функцій - фактично, пропси є єдиним аргументом для вашого компонента! Функції React-компонентів приймають єдиний аргумент, об'єкт props:

function Avatar(props) {
  let person = props.person;
  let size = props.size;
  // ...
}

Зазвичай вам не потрібен цілий об'єкт props, тому ви деструктуруєте його на окремі пропси.

Не пропустіть пару { та } curls всередині ( та ) при оголошенні пропсів:

function Avatar({ person, size }) {
  // ...
}

Цей синтаксис називається "деструктуруванням" і є еквівалентом читання властивостей з параметра функції:

function Avatar(props) {
  let person = props.person;
  let size = props.size;
  // ...
}

Вказівка значення за замовчуванням для пропса

Якщо ви хочете надати пропу значення за замовчуванням, до якого він буде повертатися, коли значення не вказано, ви можете зробити це за допомогою деструктуризації, поставивши = і значення за замовчуванням відразу після параметра:

function Avatar({ person, size = 100 }) {
  // ...
}

Тепер, якщо <Avatar person={...} /> рендериться без пропу size, то size буде встановлено як 100.

Значення за замовчуванням використовується тільки якщо відсутній проп size або якщо ви передаєте size={undefined}. Але якщо ви передасте size={null} або size={0}, буде використано значення за замовчуванням не.

Пересилання пропсів за допомогою розширеного синтаксису JSX

Іноді передача пропсів стає дуже повторюваною:

function Profile({ person, size, isSepia, thickBorder }) {
  return (
    <div className="card">
      <Avatar
        person={person}
        size={size}
        isSepia={isSepia}
        thickBorder={thickBorder}
      />
    </div>
  );
}

У повторюваному коді немає нічого поганого - він може бути більш читабельним. Але іноді ви можете цінувати лаконічність. Деякі компоненти передають усі свої пропси своїм нащадкам, як це робить Profile з Avatar. Оскільки вони не використовують жодний з своїх пропсів безпосередньо, може мати сенс використовувати більш стислий синтаксис "розкидання":

function Profile(props) {
  return (
    <div className="card">
      <Avatar {...props} />
    </div>
  );
}

Це пересилає всі пропси Profile до Avatar без перерахування кожного з них.

Обережно використовуйте розширений синтаксис. Якщо ви використовуєте його у всіх інших компонентах, щось не так. Часто це вказує на те, що вам слід розділити компоненти і передати дочірні компоненти як JSX. Детальніше про це далі!

Передача JSX як дочірніх елементів

Звично вкладати вбудовані теги браузера:

<div>
  <img />
</div>

Іноді вам може знадобитися так само вкласти власні компоненти:

<Card>
  <Avatar />
</Card>

Коли ви вкладаєте вміст у тег JSX, батьківський компонент отримає цей вміст у пропі з назвою children. Наприклад, компонент Card нижче отримає проп children зі значенням <Avatar /> і відрендерить його в обгортці div:

import Avatar from './Avatar.js';

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi',
          imageId: 'YfeOqp2'
        }}
      />
    </Card>
  );
}
import { getImageUrl } from './utils.js';

export default function Avatar({ person, size }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}
export function getImageUrl(person, size = 's') {
  return (
    'https://i.imgur.com/' +
    person.imageId +
    size +
    '.jpg'
  );
}
.card {
  width: fit-content;
  margin: 5px;
  padding: 5px;
  font-size: 20px;
  text-align: center;
  border: 1px solid #aaa;
  border-radius: 20px;
  background: #fff;
}
.avatar {
  margin: 20px;
  border-radius: 50%;
}

Спробуйте замінити <Avatar> всередині <Card> текстом, щоб побачити, як компонент Card може обгортати будь-який вкладений вміст. Йому не потрібно "знати", що рендериться всередині нього. Ви побачите цей гнучкий шаблон у багатьох місцях.

Можна уявити компонент з пропом children як "дірку", яку можна "заповнити" його батьківськими компонентами з довільним JSX. Ви часто використовуватимете дочірній проп для візуальних обгорток: панелей, сіток тощо.

Як змінюються пропси з часом

Компонент Clock нижче отримує два пропси від батьківського компонента: color та time. (Код батьківського компонента опущено, оскільки він використовує стан, у який ми поки що не будемо занурюватися)

Спробуйте змінити колір у полі вибору нижче:

export default function Clock({ color, time }) {
  return (
    <h1 style={{ color: color }}>
      {time}
    </h1>
  );
}
import { useState, useEffect } from 'react';
import Clock from './Clock.js';

function useTime() {
  const [time, setTime] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => {
      setTime(new Date());
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return time;
}

export default function App() {
  const time = useTime();
  const [color, setColor] = useState('lightcoral');
  return (
    <div>
      <p>
        Pick a color:{' '}
        <select value={color} onChange={e => setColor(e.target.value)}>
          <option value="lightcoral">lightcoral</option>
          <option value="midnightblue">midnightblue</option>
          <option value="rebeccapurple">rebeccapurple</option>
        </select>
      </p>
      <Clock color={color} time={time.toLocaleTimeString()} />
    </div>
  );
}

Цей приклад ілюструє, що компонент може отримувати різні пропси з часом. Пропси не завжди статичні! Тут проп time змінюється щосекунди, а проп color змінюється, коли ви обираєте інший колір. Пропси відображають дані компонента у будь-який момент часу, а не лише на початку.

Втім, пропси є іммутабельними - термін з інформатики, що означає "незмінні". Коли компоненту потрібно змінити свої пропси (наприклад, у відповідь на дії користувача або нові дані), йому доведеться "попросити" батьківський компонент передати йому інші пропси - новий об'єкт! Його старі пропси буде відкинуто, і зрештою рушій JavaScript поверне зайняту ними пам'ять.

Не намагайтеся "змінювати пропси".Коли вам потрібно реагувати на введення користувача (наприклад, змінювати обраний колір), вам потрібно буде "встановити стан", про який ви можете дізнатися у Стані: Пам'ять компонента.

  • Щоб передати пропси, додайте їх до JSX так само, як і атрибути HTML.
  • Для читання пропсів використовуйте синтаксис деструктуризації function Avatar({ person, size }).
  • Ви можете вказати значення за замовчуванням, наприклад size = 100, яке використовується для відсутніх та невизначених пропсів.
  • Ви можете пересилати всі пропси за допомогою <Avatar {...props} /> синтаксису поширення JSX, але не зловживайте цим!
  • Вкладені JSX на кшталт <Card><Avatar /></Card> відображатимуться як Card дочірні компоненти prop. prop.
  • Пропси є знімками лише для читання у часі: кожен рендеринг отримує нову версію пропсів.
  • Ви не можете змінювати пропси. Якщо вам потрібна інтерактивність, вам потрібно буде встановити стан.

Витягти компонент

Цей компонент Gallery містить дуже схожу розмітку для двох профілів. Витягніть з нього компонент Profile, щоб зменшити дублювання. Вам потрібно буде вибрати, які пропси йому передати.

import { getImageUrl } from './utils.js';

export default function Gallery() {
  return (
    <div>
      <h1>Notable Scientists</h1>
      <section className="profile">
        <h2>Maria Skłodowska-Curie</h2>
        <img
          className="avatar"
          src={getImageUrl('szV5sdG')}
          alt="Maria Skłodowska-Curie"
          width={70}
          height={70}
        />
        <ul>
          <li>
            <b>Profession: </b> 
            physicist and chemist
          </li>
          <li>
            <b>Awards: 4 </b> 
            (Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal)
          </li>
          <li>
            <b>Discovered: </b>
            polonium (chemical element)
          </li>
        </ul>
      </section>
      <section className="profile">
        <h2>Katsuko Saruhashi</h2>
        <img
          className="avatar"
          src={getImageUrl('YfeOqp2')}
          alt="Katsuko Saruhashi"
          width={70}
          height={70}
        />
        <ul>
          <li>
            <b>Profession: </b> 
            geochemist
          </li>
          <li>
            <b>Awards: 2 </b> 
            (Miyake Prize for geochemistry, Tanaka Prize)
          </li>
          <li>
            <b>Discovered: </b>
            a method for measuring carbon dioxide in seawater
          </li>
        </ul>
      </section>
    </div>
  );
}
export function getImageUrl(imageId, size = 's') {
  return (
    'https://i.imgur.com/' +
    imageId +
    size +
    '.jpg'
  );
}
.avatar { margin: 5px; border-radius: 50%; min-height: 70px; }
.profile {
  border: 1px solid #aaa;
  border-radius: 6px;
  margin-top: 20px;
  padding: 10px;
}
h1, h2 { margin: 5px; }
h1 { margin-bottom: 10px; }
ul { padding: 0px 10px 0px 20px; }
li { margin: 5px; }

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

У цьому рішенні компонент Profile приймає декілька пропсів: imageId (рядок), ім'я (рядок), професія (рядок), нагороди (масив рядків), відкриття (рядок) та imageSize (число).

Зверніть увагу, що проп imageSize має значення за замовчуванням, тому ми не передаємо його компоненту.

import { getImageUrl } from './utils.js';

function Profile({
  imageId,
  name,
  profession,
  awards,
  discovery,
  imageSize = 70
}) {
  return (
    <section className="profile">
      <h2>{name}</h2>
      <img
        className="avatar"
        src={getImageUrl(imageId)}
        alt={name}
        width={imageSize}
        height={imageSize}
      />
      <ul>
        <li><b>Profession:</b> {profession}</li>
        <li>
          <b>Awards: {awards.length} </b>
          ({awards.join(', ')})
        </li>
        <li>
          <b>Discovered: </b>
          {discovery}
        </li>
      </ul>
    </section>
  );
}

export default function Gallery() {
  return (
    <div>
      <h1>Notable Scientists</h1>
      <Profile
        imageId="szV5sdG"
        name="Maria Skłodowska-Curie"
        profession="physicist and chemist"
        discovery="polonium (chemical element)"
        awards={[
          'Nobel Prize in Physics',
          'Nobel Prize in Chemistry',
          'Davy Medal',
          'Matteucci Medal'
        ]}
      />
      <Profile
        imageId='YfeOqp2'
        name='Katsuko Saruhashi'
        profession='geochemist'
        discovery="a method for measuring carbon dioxide in seawater"
        awards={[
          'Miyake Prize for geochemistry',
          'Tanaka Prize'
        ]}
      />
    </div>
  );
}
export function getImageUrl(imageId, size = 's') {
  return (
    'https://i.imgur.com/' +
    imageId +
    size +
    '.jpg'
  );
}
.avatar { margin: 5px; border-radius: 50%; min-height: 70px; }
.profile {
  border: 1px solid #aaa;
  border-radius: 6px;
  margin-top: 20px;
  padding: 10px;
}
h1, h2 { margin: 5px; }
h1 { margin-bottom: 10px; }
ul { padding: 0px 10px 0px 20px; }
li { margin: 5px; }

Зверніть увагу, що вам не потрібен окремий проп awardCount, якщо awards є масивом. Тоді ви можете використовувати awards.length для підрахунку кількості нагород. Пам'ятайте, що пропси можуть приймати будь-які значення, включаючи масиви!

Іншим рішенням, більш схожим на попередні приклади на цій сторінці, є згрупування всієї інформації про людину в один об'єкт і передача цього об'єкта як одного пропса:

import { getImageUrl } from './utils.js';

function Profile({ person, imageSize = 70 }) {
  const imageSrc = getImageUrl(person)

  return (
    <section className="profile">
      <h2>{person.name}</h2>
      <img
        className="avatar"
        src={imageSrc}
        alt={person.name}
        width={imageSize}
        height={imageSize}
      />
      <ul>
        <li>
          <b>Profession:</b> {person.profession}
        </li>
        <li>
          <b>Awards: {person.awards.length} </b>
          ({person.awards.join(', ')})
        </li>
        <li>
          <b>Discovered: </b>
          {person.discovery}
        </li>
      </ul>
    </section>
  )
}

export default function Gallery() {
  return (
    <div>
      <h1>Notable Scientists</h1>
      <Profile person={{
        imageId: 'szV5sdG',
        name: 'Maria Skłodowska-Curie',
        profession: 'physicist and chemist',
        discovery: 'polonium (chemical element)',
        awards: [
          'Nobel Prize in Physics',
          'Nobel Prize in Chemistry',
          'Davy Medal',
          'Matteucci Medal'
        ],
      }} />
      <Profile person={{
        imageId: 'YfeOqp2',
        name: 'Katsuko Saruhashi',
        profession: 'geochemist',
        discovery: 'a method for measuring carbon dioxide in seawater',
        awards: [
          'Miyake Prize for geochemistry',
          'Tanaka Prize'
        ],
      }} />
    </div>
  );
}
export function getImageUrl(person, size = 's') {
  return (
    'https://i.imgur.com/' +
    person.imageId +
    size +
    '.jpg'
  );
}
.avatar { margin: 5px; border-radius: 50%; min-height: 70px; }
.profile {
  border: 1px solid #aaa;
  border-radius: 6px;
  margin-top: 20px;
  padding: 10px;
}
h1, h2 { margin: 5px; }
h1 { margin-bottom: 10px; }
ul { padding: 0px 10px 0px 20px; }
li { margin: 5px; }

Хоча синтаксис виглядає дещо інакше, оскільки ви описуєте властивості об'єкта JavaScript, а не набір атрибутів JSX, ці приклади здебільшого еквівалентні, і ви можете обрати будь-який підхід.

Відрегулювати розмір зображення на основі пропсу

У цьому прикладі Avatar отримує числовий проп size, який визначає ширину та висоту <img>. У цьому прикладі проп size має значення 40. Однак, якщо ви відкриєте зображення у новій вкладці, ви помітите, що саме зображення є більшим (160 пікселів). Реальний розмір зображення визначається тим, який розмір мініатюри ви запитуєте.

Змініть компонент Avatar так, щоб він запитував найближчий розмір зображення на основі пропси size. Зокрема, якщо size менше 90, передайте функції getImageUrl значення 's' ("small"), а не 'b' ("big"). Перевірте, чи працюють ваші зміни, відрендеривши аватари з різними значеннями пропсу size і відкривши зображення у новій вкладці.

import { getImageUrl } from './utils.js';

function Avatar({ person, size }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person, 'b')}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <Avatar
      size={40}
      person={{ 
        name: 'Gregorio Y. Zara', 
        imageId: '7vQD0fP'
      }}
    />
  );
}
export function getImageUrl(person, size) {
  return (
    'https://i.imgur.com/' +
    person.imageId +
    size +
    '.jpg'
  );
}
.avatar { margin: 20px; border-radius: 50%; }

Ось як це можна зробити:

import { getImageUrl } from './utils.js';

function Avatar({ person, size }) {
  let thumbnailSize = 's';
  if (size > 90) {
    thumbnailSize = 'b';
  }
  return (
    <img
      className="avatar"
      src={getImageUrl(person, thumbnailSize)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <>
      <Avatar
        size={40}
        person={{ 
          name: 'Gregorio Y. Zara', 
          imageId: '7vQD0fP'
        }}
      />
      <Avatar
        size={120}
        person={{ 
          name: 'Gregorio Y. Zara', 
          imageId: '7vQD0fP'
        }}
      />
    </>
  );
}
export function getImageUrl(person, size) {
  return (
    'https://i.imgur.com/' +
    person.imageId +
    size +
    '.jpg'
  );
}
.avatar { margin: 20px; border-radius: 50%; }

Ви також можете показати чіткіше зображення для екранів з високою роздільною здатністю, взявши до уваги window.devicePixelRatio:

import { getImageUrl } from './utils.js';

const ratio = window.devicePixelRatio;

function Avatar({ person, size }) {
  let thumbnailSize = 's';
  if (size * ratio > 90) {
    thumbnailSize = 'b';
  }
  return (
    <img
      className="avatar"
      src={getImageUrl(person, thumbnailSize)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <>
      <Avatar
        size={40}
        person={{ 
          name: 'Gregorio Y. Zara', 
          imageId: '7vQD0fP'
        }}
      />
      <Avatar
        size={70}
        person={{ 
          name: 'Gregorio Y. Zara', 
          imageId: '7vQD0fP'
        }}
      />
      <Avatar
        size={120}
        person={{ 
          name: 'Gregorio Y. Zara', 
          imageId: '7vQD0fP'
        }}
      />
    </>
  );
}
export function getImageUrl(person, size) {
  return (
    'https://i.imgur.com/' +
    person.imageId +
    size +
    '.jpg'
  );
}
.avatar { margin: 20px; border-radius: 50%; }

Пропси дозволяють інкапсулювати таку логіку всередині компонента Avatar (і змінювати її пізніше, якщо потрібно), щоб кожен міг використовувати компонент <Avatar>, не замислюючись над тим, як запитуються та змінюються розміри зображень.

Проходження JSX у дочірньому пропі

Витягніть компонент Card з розмітки нижче і використайте проп children для передачі йому різних JSX:

export default function Profile() {
  return (
    <div>
      <div className="card">
        <div className="card-content">
          <h1>Photo</h1>
          <img
            className="avatar"
            src="https://i.imgur.com/OKS67lhm.jpg"
            alt="Aklilu Lemma"
            width={70}
            height={70}
          />
        </div>
      </div>
      <div className="card">
        <div className="card-content">
          <h1>About</h1>
          <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
        </div>
      </div>
    </div>
  );
}
.card {
  width: fit-content;
  margin: 20px;
  padding: 20px;
  border: 1px solid #aaa;
  border-radius: 20px;
  background: #fff;
}
.card-content {
  text-align: center;
}
.avatar {
  margin: 10px;
  border-radius: 50%;
}
h1 {
  margin: 5px;
  padding: 0;
  font-size: 24px;
}

Будь-який JSX, який ви помістили у тег компонента, буде передано як проп children до цього компонента.

Ось як можна використовувати компонент Card в обох місцях:

function Card({ children }) {
  return (
    <div className="card">
      <div className="card-content">
        {children}
      </div>
    </div>
  );
}

export default function Profile() {
  return (
    <div>
      <Card>
        <h1>Photo</h1>
        <img
          className="avatar"
          src="https://i.imgur.com/OKS67lhm.jpg"
          alt="Aklilu Lemma"
          width={100}
          height={100}
        />
      </Card>
      <Card>
        <h1>About</h1>
        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
      </Card>
    </div>
  );
}
.card {
  width: fit-content;
  margin: 20px;
  padding: 20px;
  border: 1px solid #aaa;
  border-radius: 20px;
  background: #fff;
}
.card-content {
  text-align: center;
}
.avatar {
  margin: 10px;
  border-radius: 50%;
}
h1 {
  margin: 5px;
  padding: 0;
  font-size: 24px;
}

Ви також можете зробити title окремим пропсом, якщо хочете, щоб кожен Card завжди мав назву:

function Card({ children, title }) {
  return (
    <div className="card">
      <div className="card-content">
        <h1>{title}</h1>
        {children}
      </div>
    </div>
  );
}

export default function Profile() {
  return (
    <div>
      <Card title="Photo">
        <img
          className="avatar"
          src="https://i.imgur.com/OKS67lhm.jpg"
          alt="Aklilu Lemma"
          width={100}
          height={100}
        />
      </Card>
      <Card title="About">
        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
      </Card>
    </div>
  );
}
.card {
  width: fit-content;
  margin: 20px;
  padding: 20px;
  border: 1px solid #aaa;
  border-radius: 20px;
  background: #fff;
}
.card-content {
  text-align: center;
}
.avatar {
  margin: 10px;
  border-radius: 50%;
}
h1 {
  margin: 5px;
  padding: 0;
  font-size: 24px;
}
function Card({ children, title }) {
  return (
    <div className="card">
      <div className="card-content">
        <h1>{title}</h1>
        {children}
      </div>
    </div>
  );
}

export default function Profile() {
  return (
    <div>
      <Card title="Photo">
        <img
          className="avatar"
          src="https://i.imgur.com/OKS67lhm.jpg"
          alt="Aklilu Lemma"
          width={100}
          height={100}
        />
      </Card>
      <Card title="About">
        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
      </Card>
    </div>
  );
}
.card {
  width: fit-content;
  margin: 20px;
  padding: 20px;
  border: 1px solid #aaa;
  border-radius: 20px;
  background: #fff;
}
.card-content {
  text-align: center;
}
.avatar {
  margin: 10px;
  border-radius: 50%;
}
h1 {
  margin: 5px;
  padding: 0;
  font-size: 24px;
}
function Card({ children, title }) {
  return (
    <div className="card">
      <div className="card-content">
        <h1>{title}</h1>
        {children}
      </div>
    </div>
  );
}

export default function Profile() {
  return (
    <div>
      <Card title="Photo">
        <img
          className="avatar"
          src="https://i.imgur.com/OKS67lhm.jpg"
          alt="Aklilu Lemma"
          width={100}
          height={100}
        />
      </Card>
      <Card title="About">
        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
      </Card>
    </div>
  );
}
.card {
  width: fit-content;
  margin: 20px;
  padding: 20px;
  border: 1px solid #aaa;
  border-radius: 20px;
  background: #fff;
}
.card-content {
  text-align: center;
}
.avatar {
  margin: 10px;
  border-radius: 50%;
}
h1 {
  margin: 5px;
  padding: 0;
  font-size: 24px;
}
function Card({ children, title }) {
  return (
    <div className="card">
      <div className="card-content">
        <h1>{title}</h1>
        {children}
      </div>
    </div>
  );
}

export default function Profile() {
  return (
    <div>
      <Card title="Photo">
        <img
          className="avatar"
          src="https://i.imgur.com/OKS67lhm.jpg"
          alt="Aklilu Lemma"
          width={100}
          height={100}
        />
      </Card>
      <Card title="About">
        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
      </Card>
    </div>
  );
}
.card {
  width: fit-content;
  margin: 20px;
  padding: 20px;
  border: 1px solid #aaa;
  border-radius: 20px;
  background: #fff;
}
.card-content {
  text-align: center;
}
.avatar {
  margin: 10px;
  border-radius: 50%;
}
h1 {
  margin: 5px;
  padding: 0;
  font-size: 24px;
}
function Card({ children, title }) {
  return (
    <div className="card">
      <div className="card-content">
        <h1>{title}</h1>
        {children}
      </div>
    </div>
  );
}

export default function Profile() {
  return (
    <div>
      <Card title="Photo">
        <img
          className="avatar"
          src="https://i.imgur.com/OKS67lhm.jpg"
          alt="Aklilu Lemma"
          width={100}
          height={100}
        />
      </Card>
      <Card title="About">
        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
      </Card>
    </div>
  );
}
.card {
  width: fit-content;
  margin: 20px;
  padding: 20px;
  border: 1px solid #aaa;
  border-radius: 20px;
  background: #fff;
}
.card-content {
  text-align: center;
}
.avatar {
  margin: 10px;
  border-radius: 50%;
}
h1 {
  margin: 5px;
  padding: 0;
  font-size: 24px;
}
function Card({ children, title }) {
  return (
    <div className="card">
      <div className="card-content">
        <h1>{title}</h1>
        {children}
      </div>
    </div>
  );
}

export default function Profile() {
  return (
    <div>
      <Card title="Photo">
        <img
          className="avatar"
          src="https://i.imgur.com/OKS67lhm.jpg"
          alt="Aklilu Lemma"
          width={100}
          height={100}
        />
      </Card>
      <Card title="About">
        <p>Aklilu Lemma was a distinguished Ethiopian scientist who discovered a natural treatment to schistosomiasis.</p>
      </Card>
    </div>
  );
}
.card {
  width: fit-content;
  margin: 20px;
  padding: 20px;
  border: 1px solid #aaa;
  border-radius: 20px;
  background: #fff;
}
.card-content {
  text-align: center;
}
.avatar {
  margin: 10px;
  border-radius: 50%;
}
h1 {
  margin: 5px;
  padding: 0;
  font-size: 24px;
}