Реагировать на компонент высшего порядка, объясненный на примере Context API - PullRequest
0 голосов
/ 05 сентября 2018

Я пытаюсь понять пример использования компонентов высокого порядка с контекстным API, используется здесь .

Поскольку я довольно новичок в реагировании и функциональном программировании, я не понимаю, как на самом деле здесь работают. Мы отправляем функцию в функцию withUser, которая возвращает другую функцию, которая получает props.

function withUser(Component) {
  return function ConnectedComponent(props) {
    return (
      <UserContext.Consumer>
        {user => <Component {...props} user={user} />}
      </UserContext.Consumer>
    );
  };
}

Итак, когда мы создаем UserAvatar, мы передаем функцию, которая получает props и возвращает img.

const UserAvatar = withUser(({ user, size }) => (
  <img
    className={`user-avatar ${size || ""}`}
    alt="user avatar"
    src={user.avatar}
  />
));

Итак, в основном после этого мы можем представить, что UserAvatar будет:

const UserAvatar = props => {
        return (
          <UserContext.Consumer>
            {user => <Component {...props} user={user} />}
          </UserContext.Consumer>
        );
      };

Где Component:

({ user, size }) => (
      <img
        className={`user-avatar ${size || ""}`}
        alt="user avatar"
        src={user.avatar}
      />
    )

Я надеюсь, что пока это правильно, но то, что я не понимаю, это строка:

{user => <Component {...props} user={user} />}

Почему нам нужно сделать это как функцию, а не просто вернуть компонент, когда у нас уже есть props и мы передаем user компоненту UserAvatar здесь от компонента UserStats?

const UserStats = () => (
  <UserContext.Consumer>
    {user => (
      <div className="user-stats">
        <div>
          <UserAvatar user={user} />
          {user.name}
        </div>
        <div className="stats">
          <div>{user.followers} Followers</div>
          <div>Following {user.following}</div>
        </div>
      </div>
    )}
  </UserContext.Consumer>
);

1 Ответ

0 голосов
/ 05 сентября 2018

Пожалуй, самый простой способ - описать аналогичное понятие: функция декоратор.

// Start with a function, any function.
let add = (a, b) => a + b;

Но теперь мы хотим добавить логирование для аргументов и вернуть:

let addWithLogging = (a, b) => {
  console.log("Args are ", a, b);
  const result = a + b;
  console.log("Result is ", result);
  return result;
}

Barf. Наша простая однострочная функция теперь усложняется кучей вещей, которые совершенно случайны при добавлении двух чисел. Если большинство наших функций просты, и мы хотим добавить протоколирование ко всем из них, вычисление за пределами конверта состоит в том, что наша кодовая база удвоится. Двойной барф.

Но подождите, это JavaScript, у нас есть функции высшего порядка и мы можем извлечь декоратор:

// Here we take a function f and wrap it. We'll return a function that will
// collect the arguments, log them, perform f on them, log the result, and
// then finally return that result to the caller.
const withLogging = f => (...args) => {
  console.log("Args are ", ...args);
  const result = f(...args);
  console.log("Result is ", result);
  return result;
};

addWithLogging = withLogging(add);

React использует эту же идею с компонентами более высокого порядка, у вас есть компонент, который нуждается в некоторой дополнительной функциональности (часто это состояние и / или выполнение AJAX-вызовов). Вы не хотите все этим усложнять свой чистый простой тестируемый чисто функциональный компонент, поэтому вместо него вы используете компонент более высокого порядка. Как и описанный выше декоратор логирования, компонент более высокого порядка в вашем примере принимает компонент в качестве аргумента и возвращает анонимный чисто функциональный компонент, который фактически получает реквизиты и отображает переданный компонент, заключенный в компонент UserContext.

Вы могли бы просто сделать компонент UserContext частью JSX, возвращенного другим компонентом:

const UserAvatar = ({ user, size }) => (
  <UserContext.Consumer>
    <img
      className={`user-avatar ${size || ""}`}
      alt="user avatar"
      src={user.avatar}
    />
  </UserContext.Consumer>
);

Но теперь, как и в примере с журналированием, вы повторяете шаблон в каждом компоненте, который в этом нуждается.

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

EDIT

Должен был прочитать код более внимательно.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...