Как использовать фрагменты React

год назад·1 мин. на чтение

В этой статье рассмотрим использование фрагментов в приложениях React.

Что такое React Fragment?

React Fragment - это компонент React, который помогает нам возвращать несколько дочерних элементов без использования дополнительных DOM узлов. Если мы хотим вернуть группу элементов, то нужно обернуть их в общий тег. React компонент должен возвращать только один элемент, иначе будет ошибка. Следующий пример не будет работать, т.к. компонент возвращает несколько элементов - h1 и p.
import React from 'react';

function Welcome(props){
    return (
        <h1>Welcome to reactgo.com</h1>
        <p>Some dummy content</p>
    )
}
Фрагменты React представлены в версии 16. Давайте посмотрим на несколько примеров.

Без фрагментов React

import React from 'react';

function Welcome(props){
    return (
       <div>
        <h1>Welcome to reactgo.com</h1>
        <p>Some dummy content</p>
       </div>
    )
}
Теперь мы заменяем код компонента приветствия с помощью фрагментов.

С помощью React Fragments

import React,{Fragment} from 'react';

function Welcome(props){
    return (
       <Fragment>
         <h1>Welcome to reactgo.com</h1>
         <p>Some dummy content</p>
       </Fragment>
    )
}
Используя фрагменты, мы перестали создавать дополнительный элемент div в компоненте Welcome.

Короткий синтаксис фрагментов

Существует также краткий синтаксис для фрагментов.
import React from 'react';

function Welcome(props){

    return (
       <>
         <h1>Welcome to reactgo.com</h1>
         <p>Some dummy content</p>
       </>
    )
}
Этот синтаксис использует пустые теги для объявления фрагментов react.

Еще несколько примеров

Использование фрагментов при возврате группы дочерних компонентов.
function Button(){
    return <button>Click</button>
}

function Name(){
    return <h1>Hi react</h1>
}

function Post(){
    return (
        <div>
          <h1>Heading...</h1>
          <p>post content</p>
        </div>
    )
}

function App(){
    return (
        <>
         <Post/>
         <Name/>
         <Button/>
        </>
    )
}

11 ошибок, которых следует избегать при создании React приложений

2 года назад·7 мин. на чтение

Некоторые распространенные ошибки в разработке React приложений, которых следует избегать.

Поскольку React становится все более и более популярным, все больше и больше React разработчиков сталкиваются с различными проблемами в процессе разработки. В этой статье, мы обобщим некоторые распространенные ошибки в разработке React приложений, чтобы помочь вам их избежать. Если вы только начинаете использовать React, рекомендуется внимательно ознакомиться с этой статьей. Если вы уже используете React для разработки проектов, также рекомендуется проверить и заполнить пробелы.
Прочитав эту статью, вы узнаете, как избежать эти 11 ошибок React:
  • При рендеринге списка не используется key
  • Изменение значения состояния прямым присваиванием
  • Привязка значения состояния непосредственно к свойству value инпута
  • Использование состояния сразу после выполнения setState
  • Появление бесконечного цикла при использовании useState + useEffect
  • Отсутствие очистки побочных эффектов в useEffect
  • Неправильное использование логических операторов
  • Тип пропсов компонента не типизирован
  • Передача строк в качестве значений компонентам
  • Имя компонента не начинается с заглавной буквы
  • Неверная привязка события к элементу

Ошибка: При рендеринге списка не используется key

Проблема Когда мы впервые изучали React, мы отображали список следующим образом:
const items = [
  { id: 1, value: 'item1' },
  { id: 2, value: 'item2' },
  { id: 3, value: 'item3' },
  { id: 4, value: 'item4' },
  { id: 5, value: 'item5' }
];

const listItems = items.map((item) => {
  return <li>{item.value}</li>
});
После рендеринга консоль выдаст предупреждение, что для элементов списка необходимо указать ключ. Решение Вам просто нужно последовать этой подсказке и добавить key к каждому элементу:
const items = [
  { id: 1, value: ‘item1’ },
  { id: 2, value: ‘item2’ },
  { id: 3, value: ‘item3’ },
  { id: 4, value: ‘item4’ },
  { id: 5, value: ‘item5’ }
];

const listItems = items.map((item) => {
  return <li key={item.id}>{item.value}</li>
});
key помогает React определить, какие элементы были изменены, например, добавлены или удалены. Поэтому нам нужно установить уникальное значение ключа для каждого элемента в массиве. Для значения ключа лучше всего установить уникальное значение. В приведенном выше примере используется id. Можно использовать индекс массива, но такой подход не рекомендуется. Уникальный ключ помогает React следить за изменениями списка - какой элемент удалился или переместился.

Ошибка: Изменение значения состояния прямым присваиванием

Проблема В React нельзя назначать состояние и изменять напрямую, иначе это вызовет проблемы.
// классовый компонент

handleChange = () => {
   this.state.name = "John";
};
В этот момент будет выдано предупреждение не изменять состояние напрямую, а использовать setState(). Решение Классовые компоненты могут быть изменены с помощью setState(), а функциональные компоненты могут быть изменены с помощью useState():
// Классовые компоненты: используйте setState()
this.setState({ name: "John" });

// Функциональные компоненты:используйте useState()
const [name, setName] = useState("");
setName("John");

Ошибка: Привязка значения состояния непосредственно к свойству value инпута

Проблема Когда мы напрямую привязываем значение состояния к свойству value инпута, мы обнаружим, что независимо от того, что мы вводим в поле ввода, содержимое поля ввода не изменится.
export default function App() {
  const [count, setCount] = useState(0);
  return <input type="text" value={count} />;
}
Это связано с тем, что мы используем переменную состояния в качестве значения по умолчанию для присвоения значения <input>, а состояние в функциональном компоненте может быть изменено только функцией set*, возвращаемым useState. Таким образом, решение также очень простое, просто используйте функцию set* при изменении. Подробнее о том как работать с инпутом в React можно прочитать в этой статье. Решение Просто привяжите событие onChange к <input> и измените его, вызвав setCount:
export default function App() {
  const [count, setCount] = useState(0);
  const handleChange= (event) => setCount(event.target.value);

  return <input type="text" value={count} onChange={handleChange} />;
}

Ошибка: Использование состояния сразу после выполнения setState

Проблема Когда мы изменяем данные через setState() и сразу же хотим получить новые данные, возникнет ситуация, что возвращаются старые данные:
// Классовые компоненты

// инициализация состояния
this.state = { name: "John" };

// обновление состояния
this.setState({ name: "Hello, John!" });
console.log(this.state.name); // => John
Это связано с тем, что setState() является асинхронным. Когда setState() выполняется, реальная операция обновления будет помещена в асинхронную очередь для выполнения, а код, который будет выполняться следующим (т.е. console.log в примере), выполняется синхронно, поэтому выводимое в консоль состояние не является последним значением. Решение Просто передайте последующую операцию, которая будет выполняться как функция, в качестве второго параметра setState(), эта функция обратного вызова будет выполнена после завершения обновления.
this.setState({ name: "Hello, John!" }, () => {
  console.log(this.state.name); // => Hello, John!
});
Теперь обновленное значение выводится правильно.

Ошибка: Появление бесконечного цикла при использовании useState + useEffect

Проблема Когда мы напрямую вызываем метод set*(), возвращаемый useState() внутри useEffect(), и не устанавливаем второй параметр в useEffect(), мы столкнемся с бесконечным циклом:
export default function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    setCount(count + 1);
  });
  return <div className="App">{count}</div>;
}
После этого можно увидеть, что данные на странице обновляются, и функция useEffect() вызывается бесконечно, входя в состояние бесконечного цикла. Решение Это распространенная проблема неправильного использования useEffect(). useEffect() можно рассматривать как комбинацию трех функций жизненного цикла: componentDidMount, componentDidUpdate и componentWillUnmount в классовых компонентах. useEffect(effect, deps) принимает 2 аргумента:
  • effect функция, которая должна выполниться (побочный эффект)
  • deps массив зависимостей
При изменении массива deps выполняется функция эффекта. Чтобы изменить метод, вам нужно всего лишь передать [] в качестве второго аргумента useEffect() :
export default function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    setCount(count + 1);
  }, []);

  return <div className="App">{count}</div>;
}
Приведем 4 случая использования useEffect:
  • Если второй параметр не передан: при обновлении любого состояния будет запущена функция эффекта useEffect.
useEffect(() => {
  setCount(count + 1);
});
  • Если второй параметр - это пустой массив: функция эффекта useEffect срабатывает только при монтировании и размонтировании.
useEffect(() => {
  setCount(count + 1);
}, []);
  • Если второй параметр представляет собой массив с одним значением: функция эффекта useEffect будет запускаться только при изменении значения.
useEffect(() => {
  setCount(count + 1);
}, [name]);
  • Если второй параметр представляет собой массив c несколькими значениями: функция эффекта useEffect будет запускаться при изменении хотя бы одного из значений из списка зависимостей.
useEffect(() => {
  setCount(count + 1);
}, [name, age]);

Ошибка: Отсутствие очистки побочных эффектов в useEffect

Проблема В классовых компонентах мы используем метод жизненного цикла componentWillUnmount() для очистки некоторых побочных эффектов, таких как таймеры, слушатели событий и т. д. Решение Из функции эффекта useEffect() может быть возвращена функция очистки, которая аналогична роли метода жизненного цикла componentWillUnmount():
useEffect(() => {
  // ...
  return () => clearInterval(id);
}, [name, age]);

Ошибка: Неправильное использование логических операторов

Проблема В синтаксисе JSX/TSX мы часто используем логические значения для управления отображаемыми элементами, и во многих случаях мы используем оператор && для обработки этой логики:
const count = 0;
const Comp = () => count && <h1>Chris1993</h1>;
Мы думаем, что в это время страница будет отображать пустой контент, но на самом деле на ней отобразится 0. Решение Причина в том, что ложное выражение приводит к тому, что элементы после && пропускаются, и будет возвращено значение ложного выражения. Поэтому нужно стараться написать условие оценки как можно более полным, не полагаясь на истинное и ложное логическое значение JavaScript для сравнения:
const count = 0;
const Comp = () => count > 0 && <h1>Chris1993</h1>;
Теперь страница будет отображать пустой контент, как и ожидается.

Ошибка: Типы просов компонента не типизированы

Проблема Если компоненты, разработанные разными членами команды, не имеют четко определенных типов для просов, то для коллег будет не очевидно, как использовать компоненты, например:
const UserInfo = (props) => {
  return (
    <div>
      {props.name} : {props.age}
    </div>
  );
};
Решение
  • Определить типы пропсов компонента, используя TypeScript.
// Классовые компоненты
interface AppProps {
  value: string;
}
interface AppState {
  count: number;
}
class App extends React.Component<AppProps, AppStore> {
  // ...
}

// Функциональные компоненты
interface AppProps {
  value?: string;
}
const App: React.FC<AppProps> = ({ value = "", children }) => {
  //...
};
  • Без использования TypeScript типы пропсов могут быть определены с помощью propTypes.
const UserInfo = (props) => {
  return (
    <div>
      {props.name} : {props.age}
    </div>
  );
};
UserInfo.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number.isRequired,
};

Ошибка: Передача строк в качестве значений компонентам

Проблема Так как React имеет шаблонный синтаксис, очень похожий на HTML, часто бывает так, что числа передаются напрямую компонентам в пропсы, что приводит к неожиданному результату:
<MyComp count="99"></MyComp>
Сравнение props.count === 99 в компоненте MyComp вернет false. Решение Правильный способ должен заключаться в использовании фигурных скобок для передачи пропсов:
<MyComp count={99}></MyComp>
Передача строковых просов будет выглядеть следующим образом:
<MyComp count={"99"}></MyComp>

Ошибка: Имя компонента не начинается с заглавной буквы

Проблема Начинающие разработчики часто забывают называть свои компоненты с заглавной буквы. Компоненты, начинающиеся со строчной буквы в JSX/TSX, компилируются в элементы HTML, такие как <div /> для тегов HTML.
class myComponent extends React.component {}
Решение Просто измените первую букву на заглавную:
class MyComponent extends React.component {}

Ошибка: Неверная привязка события к элементу в классовых компонентах

Проблема
import { Component } from "react";

export default class HelloComponent extends Component {
  constructor() {
    super();
    this.state = {
      name: "John",
    };
  }
  update() {
    this.setState({ name: "Hello John!" });
  }

  render() {
    return (
      <div>
        <button onClick={this.update}>update</button>
      </div>
    );
  }
}
При нажатии на кнопку update консоль сообщит об ошибке, что невозможно прочитать свойства undefined (чтение setState) Решение Это происходит потому, что this не привязан к тому контексту, который мы ожидаем. Есть несколько решений:
  • Привязать контекст в конструкторе при помощи метода bind
constructor() {
  super();
  this.state = {
    name: "John"
  };
  this.update = this.update.bind(this);
}
  • Использовать стрелочные функции
update = () => {
  this.setState({ name: "Hello John!" });
};
  • Привязать прямо в функции рендеринга
<button onClick={this.update.bind(this)}>update</button>
  • Использовать стрелочные функции в функции рендеринга (не рекомендуется, т.к. это создает новую функцию каждый раз при рендеринге компонента, что влияет на производительность)
<button onClick={() => this.update()}>update</button>