m

Индивидуальные занятия по программированию с нуля, до уверенного Junior c возможностью удаленной работы: kolesnikovy70@gmail.com

Шаблоны React.

Содержание

Function component - Функциональный компонент.

Function components Функциональный компонент - это самый простой способ объявить повторно используемые компоненты.

Это просто функции.

function Greeting() {
  return <div>Hi there!</div>;
}

Собираем props из первого аргумента функции:

function Greeting(props) {
  return <div>Hi {props.name}!</div>;
}

Определите любое количество локальных переменных, чтобы делать то, что вам нужно в ваших функциональных компонентах.
Всегда возвращайте свой React компонент в конце - return.

function Greeting(props) {
  let style = {
    fontWeight: "bold",
    color: context.color
  };

  return <div style={style}>Hi {props.name}!</div>;
}

Установка значений по умолчанию для любых props с использованием параметров по умолчанию - defaultProps.

function Greeting(props) {
  return <div>Hi {props.name}!</div>;
}
Greeting.defaultProps = {
  name: "Guest"
};


Destructuring props - Деструктуризация свойств.

Назначение деструктуризации (Destructuring assignment)- это функция JavaScript. Она была добавлена в язык ES2015.

Так что это может показаться не знакомым.

Подумайте об этом как о противоположном от буквального назначения.

let person = { name: "chantastic" };
let { name } = person;

Работает с массивами.

let things = ["one", "two"];
let [first, second] = things;

Назначение деструктуризации часто используется в функциональных компонентах.

Эти декларации компонентов ниже эквивалентны.


function Greeting(props) {
  return <div>Hi {props.name}!</div>;
}

function Greeting({ name }) {
  return <div>Hi {name}!</div>;
}

Существует синтаксис для сбора оставшихся параметров объекта.

Этот синтаксис называется параметром rest ( Rest parameters ) и выглядит следующим образом.

Spread (...) используется для разделения коллекций на отдельные элементы, а rest, наоборот, для соединения отдельных значений в массив.


function Greeting({ name, ...restProps }) {
   return <div>Hi {name}!</div>;
}

Эти три точки (...) принимают все остальные свойства и назначают их объектам restProps.

Итак, что вы делаете с restProps, когда у вас есть это?

Продолжай читать...


Атрибуты распространения JSX - JSX spread attributes.

Атрибуты распространения - это функция JSX.

Это синтаксис для предоставления свойств объекта в качестве атрибутов JSX.

Следуя примеру деструктуризации параметров - Destructuring props, Мы можем распространять restProps над нашим <div>.

function Greeting({ name, ...restProps }) {
  return <div {...restProps}>Hi {name}!</div>;
}

Это делает Greeting супер гибким.

Мы можем передавать атрибуты DOM в Greeting и надеяться, что они будут переданы в div.

 

<Greeting name="Fancy pants" className="fancy-greeting" id="user-greeting" />

Избегайте пересылки не DOM props компонентам. Деструктуризация является популярной, поскольку она дает вам возможность разделить props, специфичные для компонентов, от DOM / платформы специфических атрибутов.

function Greeting({ name, ...platformProps }) {
  return <div {...platformProps}>Hi {name}!</div>;
}


Объединение деструктуризированных props с другими значениями.

Компоненты - это абстракции. Хорошие абстракции допускают расширение.

function MyButton(props) {
  return <button className="btn" {...props} />;
}

Это отлично работает, пока мы не попытаемся распространить его на другой класс.

<MyButton className="delete-btn">Delete...</MyButton>

В этом случае delete-btn заменяет btn. Вопросы по Атрибутам распространения JSX В этом случае, props.className переопределяет className в нашем компоненте. Мы можем изменить порядок, но теперь className никогда не будет ничем иным, как btn.

function MyButton(props) {
  return <button {...props} className="btn" />;
}

Нам нужно использовать присваивание деструктуризации для получения входящего className и слияния с базовым className. Мы можем сделать это, просто добавив все значения в массив и присоединив их к пробелу.

function MyButton({ className, ...props }) {
  let classNames = ["btn", className].join(" ");

  return <button className={classNames} {...props} />;
}

Чтобы защитить от отображения undefined в качестве имени класса, Используйте значения по умолчанию - default values.

function MyButton({ className="", ...props }) {
  let classNames = ["btn", className].join(" ");

  return <button className={classNames} {...props} />;
}


Условный рендеринг.

Вы не можете использовать выражения if / else внутри деклараций компонентов. Таким образом, условный (тернарный) оператор conditional (ternary) operator и оценка короткого замыкания Short-circuit evaluation- ваши друзья.

if


{
  condition && <span>Rendered when `truthy`</span>;
}

unless


{
  condition || <span>Rendered when `falsy`</span>;
}

if-else


{
  condition ? (
    <span>Rendered when `truthy`</span>
  ) : (
    <span>Rendered when `falsy`</span>
  );
}


Children types.

React может отображать children - детей из большинства типов. В большинстве случаев это либо array- массив, либо string - строка.

String


<div>Hello World!</div>

String


<div>{["Hello ", <span>World</span>, "!"]}</div>


Массив как дети - Array as children.

Предоставление массива в качестве детей является очень распространенным явлением. Это как списки в React. Мы используем map () для создания массива React Elements для каждого значения в массиве.

<ul>
  {["first", "second"].map(item => (
    <li>{item}</li>
  ))}
</ul>

Это эквивалентно предоставлению литералa массива array.

<ul>{[<li>first</li>, <li>second</li>]}</ul>

Этот шаблон можно комбинировать с деструктурированием, атрибутами распространения JSX и другими компонентами для некоторого серьезного усиления.

<ul>
  {arrayOfMessageObjects.map(({ id, ...message }) => (
    <Message key={id} {...message} />
  ))}
</ul>


Функции как дети - Function as children.

Компоненты React не поддерживают функции как children -дети. Однако Render prop - это шаблон для создания компонентов, которые выполняют функции как дети.




Рендеринг свойств - Render prop.

Вот компонент, который использует обратный вызов - render callback. Это не полезно, но для начала это простая иллюстрация.


const Width = ({ children }) => children(500);



Компонент вызывает children как функцию с некоторым количеством аргументов. Здесь это число 500.

Чтобы использовать этот компонент, мы даем ему Функции как дети - Function as children.


<Width>{width => <div>window is {width}</div>}</Width>



Мы получаем этот результат.


<div>window is 500</div>



С помощью этой настройки мы можем использовать эту ширину для принятия решений.


<Width>
  {width => (width > 600 ? <div>min-width requirement met!</div> : null)}
</Width>



Если мы планируем использовать это условие несколько раз, то мы можем определить другие компоненты для инкапсуляции повторно используемой логики.


const MinWidth = ({ width: minWidth, children }) => (
  <Width>{width => (width > minWidth ? children : null)}</Width>
);



Очевидно, что статический компонент Width не полезен, но тот, который смотрит в окно браузера. Вот пример реализации.


class WindowWidth extends React.Component {
  constructor() {
    super();
    this.state = { width: 0 };
  }

  componentDidMount() {
    this.setState(
      { width: window.innerWidth },
      window.addEventListener("resize", ({ target }) =>
        this.setState({ width: target.innerWidth })
      )
    );
  }

  render() {
    return this.props.children(this.state.width);
  }
}

Многие разработчики предпочитают компоненты Higher Order Components - более высокого порядка для такого типа функциональности. Это вопрос предпочтения.

Children pass-through

Вы можете создать компонент, предназначенный для применения контекста и отображения его дочерних элементов.

class SomeContextProvider extends React.Component {
  getChildContext() {
    return { some: "context" };
  }

  render() {
    // how best do we return `children`?
  }
}

Вы столкнулись с решением. Оберните children в <div /> или верните (return) children напрямую. В первом случае, это даст дополнительную разметку (которая может разорвать некоторые таблицы стилей). В другом, приведет к бесполезным ошибкам.

 // option 1: extra div
 return <div>{children}</div>;

 // option 2: unhelpful errors
return children;

Лучше рассматривать children как непрозрачный тип данных. Реакт предоставляет React.Children для работы с children надлежащим образом.

return React.Children.only(this.props.children);


Прокси-компонент. Proxy component.

(Я не уверен, имеет ли это название смысл) Кнопки доступны везде в веб-приложениях. И каждая из них должна иметь атрибут type, установленный как "button".

<button type="button">

Написание этого атрибута сотни раз подвержено ошибкам. Мы можем написать компонент более высокого уровня для прокси-props для нижнего уровня button компонента.

const Button = props =>
  <button type="button" {...props}>

Мы можем использовать Button вместо button и гарантировать, что атрибут type постоянно применяется везде.

<Button />
// <button type="button"><button>

<Button className="CTA">Send Money</Button>
// <button type="button" class="CTA" >Send Money</button>


Компонент стиля - Style component.

Это прокси-компонент, применяемый к практике стиля. Скажем, у нас есть кнопка. Он использует классы для стилизации как "primary" кнопки.

<button type="button" className="btn btn-primary">

Мы можем генерировать этот результат, используя пару одноцелевых компонентов.

import classnames from "classnames";

const PrimaryBtn = props => <Btn {...props} primary />;

const Btn = ({ className, primary, ...props }) => (
  <button
    type="button"
    className={classnames("btn", primary && "btn-primary", className)}
    {...props}
  />
);

Это может помочь визуализировать это.

PrimaryBtn()
  ↳ Btn({primary: true})
    ↳ Button({className: "btn btn-primary"}, type: "button"})
      ↳ '<button type="button" class="btn btn-primary"></button>'

Используя эти компоненты, все они приводят к одному и тому же результату.

<PrimaryBtn />
<Btn primary />
<button type="button" className="btn btn-primary" />

Это может быть огромным благом для поддержания стиля. Он изолирует все проблемы стиля до одного компонента.

Переключатель событий - Event switch.

При написании обработчиков событий принято применять соглашение об именах handle {eventName}.

handleClick(e) { /* do something */ }

Для компонентов, которые обрабатывают несколько типов событий, эти имена функций могут повторяться. Сами имена могут не обеспечить большую ценность, поскольку они просто проксируют другие действия / функции.

handleClick() { require("./actions/doStuff")(/* action stuff */) }
handleMouseEnter() { this.setState({ hovered: true }) }
handleMouseLeave() { this.setState({ hovered: false }) }

Подумайте о написании одного обработчика событий для своего компонента и включении event.type.

handleEvent({type}) {
  switch(type) {
    case "click":
      return require("./actions/doStuff")(/* action dates */)
    case "mouseenter":
      return this.setState({ hovered: true })
    case "mouseleave":
      return this.setState({ hovered: false })
    default:
      return console.warn(`No case for event type "${type}"`)
  }
}

В качестве альтернативы, для простых компонентов вы можете вызвать импортированные действия / функции непосредственно из компонентов, используя функции стрелок.

<div onClick={() => someImportedAction({ action: "DO_STUFF" })}

Не беспокойтесь об оптимизации производительности, пока у вас не возникнут проблемы. Серьезно не надо.

Компонент шаблона (макета) - Layout component.

Компоненты шаблона приводят к некоторой форме статического элемента DOM. Возможно, их не потребуется часто обновлять, если вообще когда-либо придется. Рассмотрим компонент, который создает два children рядом - бок о бок.

<HorizontalSplit
  leftSide={<SomeSmartComponent />}
  rightSide={<AnotherSmartComponent />}
/>

Мы можем агрессивно оптимизировать этот компонент. Хотя HorizontalSplit будет родительским - parent для обоих компонентов, он никогда не станет их владельцем. Мы можем сказать, что он никогда не обновится, не прерывая жизненный цикл компонентов внутри.

class HorizontalSplit extends React.Component {
  shouldComponentUpdate() {
    return false;
  }

  render() {
    <FlexContainer>
      <div>{this.props.leftSide}</div>
      <div>{this.props.rightSide}</div>
    </FlexContainer>;
  }
}


Контейнерный компонент - Container component.

«Контейнер делает выборку данных, а затем отображает соответствующий субкомпонент. Вот и все».

Джейсон Бонта



Учитывая этот многократный компонент CommentList.

const CommentList = ({ comments }) => (
  <ul>
    {comments.map(comment => (
      <li>
        {comment.body}-{comment.author}
      </li>
    ))}
  </ul>
);

Мы можем создать новый компонент, ответственный за выборку данных и отображение компонента функции CommentList.

class CommentListContainer extends React.Component {
  constructor() {
    super()
    this.state = { comments: [] }
  }

  componentDidMount() {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: comments =>
        this.setState({comments: comments});
    })
  }

  render() {
    return <CommentList comments={this.state.comments} />
  }
}

Мы можем писать разные контейнеры для разных контекстов приложений.

Компонент высшего порядка - Higher-order component.

Функция более высокого порядка (higher-order function)- это функция, которая принимает и / или возвращает функцию. Это несложно. Итак, что такое компонент более высокого порядка?

Если вы уже используете container components, это просто общие контейнеры, завернутые в функцию.
Начнем с нашего компонента Greeting.

const Greeting = ({ name }) => {
  if (!name) {
    return <div>Connecting...</div>;
  }

  return <div>Hi {name}!</div>;
};

Если он получит props.name, он будет отображать эти данные. В противном случае он скажет, что это «Подключение ...». Теперь для бит более высокого порядка.

const Connect = ComposedComponent =>
  class extends React.Component {
    constructor() {
      super();
      this.state = { name: "" };
    }

    componentDidMount() {
      // this would fetch or connect to a store
      this.setState({ name: "Michael" });
    }

    render() {
      return <ComposedComponent {...this.props} name={this.state.name} />;
    }
  };

Это просто функция, которая возвращает компонент, который отображает компонент, который мы передали в качестве аргумента.

Последний шаг, нам нужно обернуть наш компонент Greeting в Connect.

const ConnectedMyComponent = Connect(Greeting);

Это мощный шаблон для обеспечения выборки и предоставления данных для любого количества функциональных компонентов.

Подъем состояния - State hoisting.

Функциональный компонент не содержит состояния (как следует из названия).

События - это изменения в состоянии. Их данные должны быть переданы родительским элементам компонента контейнера с состоянием.

Это называется «подъемом состояния» (State hoisting). Это достигается путем передачи обратного вызова из компонента контейнера в дочерний компонент.

class NameContainer extends React.Component {
  render() {
    return <Name onChange={newName => alert(newName)} />;
  }
}

const Name = ({ onChange }) => (
  <input onChange={e => onChange(e.target.value)} />
);


Name получает обратный вызов onChange от NameContainer и вызывает события.

Приведенное выше предупреждение alert представляет собой краткую демонстрацию, но она не меняет состояние. Давайте изменим внутреннее состояние NameContainer.

class NameContainer extends React.Component {
  constructor() {
    super();
    this.state = { name: "" };
  }

  render() {
    return <Name onChange={newName => this.setState({ name: newName })} />;
  }
}


С помощью предоставленного обратного вызова состояние переносится - поднимается (hoisted) в контейнер, где оно используется для обновления локального состояния. Это устанавливает хорошую четкую границу и максимизирует возможность повторного использования компонента функции.

Этот шаблон не ограничен функциональными компонентами. Поскольку компоненты функций не имеют событий жизненного цикла, вы будете использовать этот шаблон и с классами компонентов.

Контролируемый ввод (Controlled input ) является важным шаблоном, который нужно знать для использования с подъемом состояния

(Лучше всего обрабатывать объект события в компоненте с состоянием)

Контролируемый ввод - Controlled input .

Трудно говорить об управляемых инпутах в резюме. Давайте начнем с неконтролируемого (нормального) ввода и пойдем оттуда.

<input type="text" />


Когда вы возитесь с этим вводом в браузере, вы видите ваши изменения. Это нормально.

Контролируемый ввод запрещает мутации DOM, которые делают это возможным. Вы устанавливаете значение ввода в компонентной области, а оно не изменяется в DOM-области.

<input type="text" value="This won't change. Try it." />


Очевидно, что статические инпуты не очень полезны для ваших пользователей. Итак, мы получаем значение от состояния state.

class ControlledNameInput extends React.Component {
  constructor() {
    super();
    this.state = { name: "" };
  }

  render() {
    return <input type="text" value={this.state.name} />;
  }
}


Затем, изменение инпута - это вопрос изменения состояния компонента.

return (
  <input
    value={this.state.name}
    onChange={e => this.setState({ name: e.target.value })}
  />
);
Это контролируемый вход. Он обновляет DOM только после изменения состояния в нашем компоненте. Это бесценно при создании согласованных интерфейсов.

Если вы используете Function component - Функциональный компонент. для элементов формы, прочитайте об использовании подъема состояния - State hoisting. для перемещения нового состояния вверх по дереву компонентов.





Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.


Удачного кодирования!




Выражаю особую благодарность Flavio Copes, который вдохновил меня на этот труд.                                                                                                                                                              

Телеграм канал - Full Stack JavaScript Developer
Помочь проекту (любая валюта). DONATE

Шаблоны React.

Содержание Функциональный компонент - Function component. Деструктуризация свойств - Destructuring props. Атрибуты распространения JSX...