Перейти к содержанию

20.1 React

Создание проекта

1
2
3
4
5
npm create vite@latest

npm i

npm run dev

Компоненты

  • React позволяет проще строить интерфейсы с помощью компонентов
  • Компонент это функция, которая возвращает JSX
  • JSX это синтаксический сахар, который React преобразует в JS
  • Компоненты могут содержать состояние

Плюсы компонентов

  • Переиспользуемость
  • Разделение ответственность
  • Удобное тестирование

Props

  • Props предназначен для передачи данных между компонентами
  • Это позволяет эффективно переиспользовать компоненты
import JournalItem from "./JournalItem";

function App() {
  const data = [
    {
      title: "Подготовка обновления курсов",
      date: new Date(),
      text: "Горные походы открывают удвительные природные ландшафты",
    },
    {
      title: "Подготовка обновления курсов2",
      date: new Date(),
      text: "Горные походы 222 открывают удвительные природные ландшафты",
    },
  ];

  return (
    <>
      <JournalItem
        title={data[0].title}
        text={data[0].text}
        date={data[0].date}
      />
    </>
  );
}
  function JournalItem({ title, text, date }) {
  const formatedDate = new Intl.DateTimeFormat("ru-RU").format(date);

  return (
    <div className="journal-item">
      <h2 className="journal-item__header">{title}</h2>
      <h2 className="journal-item__body">
        <div className="journal-item__date">{formatedDate}</div>
        <div className="journal-item__text">{text}</div>
      </h2>
    </div>
  );
}

export default JournalItem;

Обработка событий

1
2
3
4
5
6
function Button({ children, onClick }) {
    console.log('Button');
    return (
        <button className='button accent' onClick={onClick}>{children}</button>
    );
}

useState

  • Это функция которая возвращает кортеж состояния и функцию для обновления состояния
  • Значение меняется на сразу, а будет запланировано к изменению react'ом
  • При повторном рендиренге useState не будет присваивать дефолтное значение
1
2
3
4
  const [text, setText] = useState("Save");

  // Для использования текущего "старого состояния"
  setText(oldText => oldText + "!");

useReducer

  • useReducer() это альтернатива useState() которая дает больший контроль над управлением состояния и упрощает тестирование
  • Все примеры можно реализовать и с помощью useState()
function reducer(state, action) {
  switch (action.type) {
    case 'ADD': return { count: state.count + 1 };
    case 'SUB': return { count: state.count - 1 };
    default: return state;
  }
}

function Counter() {
  const [state, dispatch] = React.useReducer(reducer, { count: 0 });
  return (
    <>
      Итого: {state.count}
      <button onClick={() => dispatch({type: 'ADD'})}>Добавить</button>
      <button onClick={() => dispatch({type: 'SUB'})}>Вычесть</button>
    </>
  );
}

useContext

  • Обычно контекст используется, если необходимо обеспечить доступ данных во многих компонентах на разных уровнях вложенности.
  • По возможности не используйте его, так как это усложняет повторное использование компонентов.
  • Чтобы определить данные, которые мы хотим отправлять, мы должны начать с поставщика контекста ().
  • Затем определяется потребитель контекста (), который захватывает эти данные и использует их там, где они вызываются.
const { userId } = UserContext(UserContext);

Простая декомпозиция на компоненты

function App() {
    return (
        <div className='app'>
            <LeftPanel>
                <Header/>
                <JournalAddButton/>
                <JournalList/>
            </LeftPanel>
            <Body>
                <JournalForm/>
            </Body>
        </div>
    );
}

Работа с формами

function JournalForm() {

    const addJournalItem = (e) => {
        e.preventDefault();
        const formData = new FormData(e.target);
        const formProps = Object.fromEntries(formData);
        // Метод Object.fromEntries() static преобразует список пар ключ-значение в объект.
        console.log('submit');
    };

    return (
        <form className={styles['journal-form']} onSubmit={addJournalItem}>
            <input type="text" placeholder="Заголовок" onChange={inputChange}/>
            <textarea></textarea>
            <Button>Сохранить</Button>
        </form>
    );
}

Работа со списком данных

function App() {
    const data = [
        {   
            id : 1,
            title: "Подготовка обновления курсов",
            date: new Date(),
            text: "Горные походы открывают удвительные природные ландшафты",
        },
        {
            id : 2,
            title: "Подготовка обновления курсов2",
            date: new Date(),
            text: "Горные походы 222 открывают удвительные природные ландшафты",
        },
        ];
        return (
        <>
            {data.sort(sortItems).map(el => (
                <JournalItem key={el.id}
                    title={el.title}
                    text={el.text}
                    date={el.date}
                />
            ))}
        </>
    );
}

Note

key это важный параметры для элемента списка Ключи помогают React определять, какие элементы были изменены, добавлены или удалены.

Условный рендер в JSX

1
2
3
4
5
condition && <JSX /> рендерит <JSX /> если условие condition вернет true
condition ? <JsxTrue /> : <JsxFalse /> рендерит <JsxTrue /> или <JsxFalse> в зависимости от того, что вернет условие condition.

{items.length === 0 && <p>Записей пока нет</p>}
{items.length > 0 && items.map(el => ())}

Правильная работа с CSS модулями

1
2
3
import styles from './JournalItem.module.css';

<img className={styles.image}>

Библиотека classNames Основние использование когда нужно добавить классы по условию

1
2
3
import classNames from 'classnames';

className={classNames(styles['image'], {[styles['image--small']]: ifSmall})}

useEffect

  • Используя этот хук, вы говорите React сделать что-то после рендера.
  • React запомнит функцию (то есть «эффект»), которую вы передали и вызовет её после того, как внесёт все изменения в DOM.
  • useEffect говорит что мы должны вызвать какой это эффект при изменении наших зависимостей.
  • Если зависимости пустые значит он больше никогда не будет триггериться.
1
2
3
4
5
6
7
const [items, setItems] = useState([]);

const data = JSON.parse(localStorage.getItem('data'));

if (data) {
    setItems(data);
}

Warning

Такая конструкция вызовет бесконечный ререндинг Для того чтобы это не происходило, нужно использовать useEffect

1
2
3
useEffect(() => {
    console.log('useEffect');
    }, [items]);
<html>
<body>

<div class="wrapper">

  <div class="content"></div>

  <div class="footer"></div>

</div>

</body>
</html>
html,
body {
  height: 100%;
}
.wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.content {
  flex: 1 0 auto;
}
.footer {
  flex: 0 0 auto;
}