Skip to content

Latest commit

 

History

History
152 lines (108 loc) · 5.49 KB

17. Паттерны: от Higher-Order Components до Render Props.md

File metadata and controls

152 lines (108 loc) · 5.49 KB

Паттерны: от Higher-Order Components до Render Props

Вместо собственных велосипедов давайте использовать уже готовые подходы

Паттерны Реакта

Вместо собственных велосипедов давайте использовать уже готовые подходы

Начнём, на удивление, с самого простейшего вопроса: как отрендерить кусок в зависимости от проверки?

Как вы помните, Джсх это всё равно что Джс, поэтому в самом примитивном варианте просто вставьте ифы и ретёрны.

Примитивный вариант

if (x) return <TruthyComp />;

// вернется если проверка на x
// не отработала в true
return <FalsyComp />;

Ну или тернарный оператор ?:

return x ? <TruthyComp /> : <FalsyComp />;

Что делать, если у вас большой компонент (не надо так) и нужно по условию отрендерить какой-то блок? Допустим, кнопку. Просто поместите проверку через оператор && в {}.

function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 && (
        <h2>You have {unreadMessages.length} unread messages.</h2>
      )}
    </div>
  );
}

Как замечено в официальной документации, такое возможно, потому что в Джаваскрипте выражение true && expression выполнит expression, а false && expression выполнит false.

Частичные компоненты

Создаёте компонент вокруг другого компонента, передав ему какой-то набор пропов.

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

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

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

Чилдрен может быть функцией. Знали?

class Auth extends React.Component {
  state = {
    isLoggedIn: false,
    userId: 1
  };

  render() {
    const { isLoggedIn, userId } = this.state;

    return this.props.children({ isLoggedIn, userId });
  }
}

function Lesson(props) {
  return (
    <View>
      <h1>{props.title}</h1>

      <Auth>
        {function({ isLoggedIn, userId }) {
          if (isLoggedIn) {
            return props.lesson;
          }

          // возвращаем превью урока если человек не залогинен
          return props.preview;
        }}
      </Auth>
    </View>
  );
}

Тот же самый паттерн используется в рендер пропе: просто вместо чилдрен используется проп render и код выглядит как <Auth render={function() {...}} />.

Ещё один способ передать пропы из одного компонента в другой: HoC.

HoC — это функция, в которую приходит компонент и которая возвращает другой компонент.

Из примеров: connect() в react-redux и withRouter() в Реакт-роутере.

function withAuth(Comp, options = {}) {
  return class extends React.Component {
    state = {
      isLoggedIn: false,
      userId: 1
    };

    render() {
      const { isLoggedIn, userId } = this.state;

      return <Comp isLoggedIn={isLoggedIn} userId={userId} />;
    }
  };
}

function Lesson(props) {
  return (
    <View>
      <h1>{props.title}</h1>

      {this.props.isLoggedIn ? props.lesson : props.preview}
    </View>
  );
}

const LessonWithAuth = withAuth(Lesson);

В чём отличие от рендер пропа? В том, что менее явное поведение: в рендер пропе обычная функция с набором аргументов, а тут в пропы нашего компонента спускаются неизвестные пропы.

Родитель-ребенок

Здесь оставлю ссылку на пост Юли, которая проходила курс: она расписала модели «родитель → ребенок», «ребенок → родитель» и «ребенок → ребенок».

Итог

Паттерны нужны, чтобы не плодить свои велосипеды и не усложнять себе (и другим разработчикам!) жизнь.

Используйте всё кроме HoC — за лаконичность их АПИ расплачиваетесь неочевидностью пропов.