Время от времени мне приходится проводить собеседования. И сегодня я хочу поделиться моими любимыми вопросами на тему Redux. К сожалению, вопросы уже устарели, т.к. они касаются компонента высшего порядка connect, который активно заменяют на хуки. Но connect может уже и не сильно актуален, а принципы на которых он построен абсолютно не изменились (Данная статья является расшифровкой видео).

И так давайте перейдем к самим вопросам

После общего вопроса: “Что такое Redux?”. Я обычно спрашивал: “Какой первый параметр принимает connect?”. Тут все отвечают правильно: “mapStateToProps”. Но вот на вопрос, а что такое "mapStateToProps", какой это тип данных. Некоторые уже начинают отвечать неправильно.

Многие возможно немного волнуясь на собеседовании, начинают отвечать, что mapStateToProps - это объект. И после пары уточняющих вопросов, конечно исправляются на то, что mapStateToProps - это функция, которая первым параметром получает весь state и возвращает нужные компоненту данные.

Главный вопрос

Хорошо mapStateToProps это функция. Тогда давайте представим следующую ситуацию. Допустим у нас на странице несколько независимых блоков. Например один блок это список пользователей, другой блок список машин, третий блок это список квартир и так далее. В итоге на странице несколько абсолютно независимых блоков. Каждый блок обернут в свой connect и тянет только информацию своего блока.

// Users.js
const mapStateToProps = state => ({
  users: state.users
})
export default connect(mapStateToProps)(Users)

// Cars.js
const mapStateToProps = state => ({
  cars: state.cars
})
export default connect(mapStateToProps)(Cars)

// Apartments.js
const mapStateToProps = state => ({
  apartments: state.apartments
})
export default connect(mapStateToProps)(Apartments)

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

И так, главный вопрос: "сколько разных функций mapStateToProps при этом вызовется?" Добавлю даже варианты ответа:

Осторожно ниже ответ!

3...

2...

1...

И правильный ответ...

Номер один. На этот вопрос многие дают ответ номер два.

По их мнению mapStateToProps вызывается только у компонента, контент которого обновился. И это звучит на первый взгляд очень логично. Но дальше можно задать уточняющий вопрос: “А как тогда redux понимает, какой именно нужно вызвать mapStateToProps?”. И этот вопрос, чаще всего, заставляет разработчиков задуматься, о том, что они возможно неправильно ответили на предыдущий вопрос.

Давайте вспомним как выглядит функция mapStateToProps.

const mapStateToProps = (state) => {
  return {
    users: state.users
  }
}

Она принимает в качестве параметра переменную state, в которой лежит состояние всего стора. И только в этой функции мы определяем какие именно данные нужно передать в компонент. Это значит, что до вызова функции mapStateToProps, redux понятия не имеет, нужно рендерить компонент обернутый в connect или не нужно.

Если визуализировать картину это будет выглядеть следующим образом:

Action обновляет store. Store в свою очередь вызывает все зарегистрированные функции mapStateToProps, которые передают нужные компонентам данные в connect. И далее лишь один connect заставит обновиться компонент пользователей. Вот так работает наш пример.

Как это работает под капотом

Параметры mapStateToProps

Хорошо с mapStateToProps стало более менее понятно, как это работает. Но теперь хочется понять, как именно connect понимает нужно ему обновлять компонент, который он оборачивает или нет. Для того чтобы в этом разобраться мы изучим как работает connect под капотом.

Но перед этим, еще один мини вопрос (это для тех кто мнит себя знатаком инструментов). Мы знаем, что connect принимает несколько параметров, таких как mapStateToProps, mapDispatchToProps. Вопрос, сколько параметров можно передать в connect? Оборачиваемый компонент не считается за параметр. И так варианты ответа: 2? 3? 4? или 5?

Место на подумать...

3...

2...

1...

И правильный ответ connect принимает целых 4 параметра. Давайте посмотрим исходники и убедимся в этом (ссылка на исходники).

function connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
  options
) {
  // ...
}

Первые два нам хорошо известны. Далее менее популярный mergeProps, который позволяет нам сгруппировать данные полученные из первых двух функций.

И практически не используемый никем 4-ый параметр. И он представляет из себя объект, через который можно донастроить работу вашего connect.

{
    // The `pure` option has been removed, so TS doesn't like us destructuring this to check its existence.
    // @ts-ignore
    pure,
    areStatesEqual = strictEqual,
    areOwnPropsEqual = shallowEqual,
    areStatePropsEqual = shallowEqual,
    areMergedPropsEqual = shallowEqual,

    // use React's forwardRef to expose a ref of the wrapped component
    forwardRef = false,

    // the context consumer to use
    context = ReactReduxContext,
  }

Из интересных в нем настроек, я хотел бы обратить внимание на эту 4-ку.

{
  areStatesEqual = strictEqual,
  areOwnPropsEqual = shallowEqual,
  areStatePropsEqual = shallowEqual,
  areMergedPropsEqual = shallowEqual,
}

Поясню, что каждое поле означает. areStatesEqual - это функция, которая сравнивает изменился ли redux store. Поэтому по дефолту у него значение strictEqual, а не shallowEqual, т.к. нам нужно знать о любом изменении объекта, даже если это произошло где то глубоко.

Далее идет areOwnPropsEqual - это функция, которая сравнивает props которые передали в компонент обернутый в connect.

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

И последняя функция areMergePropsEqual - которая сравнивает результаты выполнения функции mergeProps.

Ядро connect-а

Проходиться по всему коду connect займет много времени, поэтому я перейду сразу к интересному месту. А именно, где эти функции вызываются. Есть одна фабрика, которая возвращает такую не хитрую функцию как pureFinalPropsSelector (ссылка на исходники).

export function pureFinalPropsSelectorFactory(/* ... */) {
  // ....
  
  return function pureFinalPropsSelector(
    nextState: State,
    nextOwnProps: TOwnProps
  ) {
    return hasRunAtLeastOnce
      ? handleSubsequentCalls(nextState, nextOwnProps)
      : handleFirstCall(nextState, nextOwnProps)
  }
}

В качестве параметров она принимает nextState - это состояние всего стора и nextOwnProps - это собственно props переданные в компонент. И возвращает эта функция результат выполнения одной из двух функций. В зависимости первый раз рендериться этот компонент или нет.

Посмотрим для начала функцию, которая вызывается на первом рендере.

function handleFirstCall(firstState: State, firstOwnProps: TOwnProps) {
  state = firstState
  ownProps = firstOwnProps
  // @ts-ignore
  stateProps = mapStateToProps(state, ownProps)
  // @ts-ignore
  dispatchProps = mapDispatchToProps!(dispatch, ownProps)
  mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
  hasRunAtLeastOnce = true
  return mergedProps
}

Функция, вычисляет значения mapStateToProps, mapDispatchToProps, mergeProps переключает флаг hasRunAtLeastOnce на true и на этом первый рендер окончен. Возвращает эта функция mergedProps, который мы и получаем в нашем компоненте.

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

function handleSubsequentCalls(nextState: State, nextOwnProps: TOwnProps) {
  const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)
  const stateChanged = !areStatesEqual(nextState, state)
  state = nextState
  ownProps = nextOwnProps

  if (propsChanged && stateChanged) return handleNewPropsAndNewState()
  if (propsChanged) return handleNewProps()
  if (stateChanged) return handleNewState()
  return mergedProps
}

Сначала она сравнивает не изменились ли props.

const propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps)

Потом проверяет не изменился ли сам redux store.

const stateChanged = !areStatesEqual(nextState, state)

И в зависимости от того что именно изменилось, уже решает, какую именно функцию вызывать. Самый популярный случай, это все же изменения именно store. Поэтому мы рассмотрим функцию handleNewState.

function handleNewState() {
  const nextStateProps = mapStateToProps(state, ownProps)
  const statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps)
  // @ts-ignore
  stateProps = nextStateProps

  if (statePropsChanged)
    mergedProps = mergeProps(stateProps, dispatchProps, ownProps)

  return mergedProps
}

Код достаточно простой. Вычисляется новый результат выполнения функции mapStateToProps и далее с помощью той самой функции areStatePropsEqual сравнивается с предыдущим значением. И если statePropsChanged равно true, тогда мержатся все props в один объект и отдаются обернутому компоненту.

Чтобы вы не пугались функции mergeProps. Если мы не передаем ее третьим параметром, в этом случае по дефолту она выглядит вот так:

export function defaultMergeProps<TStateProps, TDispatchProps, TOwnProps>(
  stateProps: TStateProps,
  dispatchProps: TDispatchProps,
  ownProps: TOwnProps
) {
  return { ...ownProps, ...stateProps, ...dispatchProps }
}

Согласитесь, крайне просто.

Подытожить исследования исходников connect можно мыслью, что вся магия держится на одном простом сравнении результата выполнения mapStateToProps с помощью shallowEqual. Но, надо обязательно помнить, что shallowEqual имеет свои ограничения.

Как не стоит делать

Давайте рассмотрим несколько неудачных примеров:

const mapStateToProps = (state) => {
  const { user } = state

  return {
    user: {
      ...user,
      name: prepareNameToDisplay(user),
    }
  }
}    

На первый взгляд этот код выглядит вполне себе жизнеспособным. Пользователю на экране мы хотим показать имя состоящее из нескольких свойств.

name: prepareNameToDisplay(user)

Так делать не рекомендуется. Вспомним о том как работает shallowEqual. В нашем случае, на каждый вызов mapStateToProps, мы возвращаем новую ссылку на юзера, т.к. мы создаем новый объект каждый раз. Проблема именно в этих фигурных скобках.

return {
  user: { // <-- вот в этих скобках
    ...user,
    name: prepareNameToDisplay(user),
  } // <-- вот в этих скобках
}

В результате компонент, который мы обернули в connect будет рендериться в 100% случаев, когда обновляется store и даже, если обновился не user, а какие-нибудь другие данные.

const mapStateToProps = (state) => {
  const { apartments } = state

  return {
    apartments: apartments.filter(apartment => apartment.price >= 100)
  }
}

Вся загвоздка в методе filter, который всегда возвращает новый массив, а не мутирует предыдущий. Таким образом shallowEqual всегда будет возвращать false.

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

Решить эту проблему можно разными путями. Но один из новых путей, с которыми мы сегодня познакомились, вероятно не самый эффективный, это передать в текущий connect 4-ым параметром функцию areStatePropsEqual, где мы сами опишем как лучше сравнивать значения между рендерами.

export default connect(mapStateToProps, mapDispatchToProps, null, {
  areStatePropsEqual: someCustomFunction,
})(Users);

Если же вы не поняли, в чем именно кроется проблема, я бы рекомендовал более детально изучить тему “передача параметров по ссылке и по значению в javascript” и после погуглить "как работает метод shallowEqual". И когда вы усвоите материал вернуться к этой статье и возможно для вас откроется много нового.

Дисклеймер

В этой статье я хотел поделиться с вами тем, как работает connect под капотом. Все слова про собеседования это действительно правда, но не основная тема этой статьи.

Комментарии (63)


  1. Alexandroppolus
    16.11.2021 09:58
    +7

    "главный вопрос" - не такой уж устаревший. При использовании useSelector тоже все селекторы вызываются, если поменялся стейт в редуксе.


    1. Sin9k Автор
      16.11.2021 10:48
      +1

      Все верно) Я теперь спрашиваю про useSelector как работает))
      На эту тему будет следующее видео / статья)


    1. DarthVictor
      17.11.2021 22:47

      В контексте статьи важный вопрос при использовании useSelector, какую функцию сравнения стейтов он использует по умолчанию. Потому что не ту же самую, что и connect.


      1. Sin9k Автор
        17.11.2021 23:06

        Про useSelector я запланировал делать отдельное видео. Уже изучил его исходники. Поэтому подписывайтесь на АйТи Синяка и все увидите)


  1. shybovycha
    16.11.2021 14:13
    +24

    Какой первый параметр принимает connect

    Считаю что на подобные вопросы кандидаты уровня мида и выше могут с уверенностью отвечать "не помню, щас загуглю".

    Лично я бы такой вопрос даже не задавал. Лучше узнать умеет ли человек спроектировать хранилище данных впринципе и начать задание примерно с середины статьи, в ключе "нужно сделать страничку со списками пользователей, питомцев и квартир с возможностью удаления элементов списка. Реализуйте в том, в чем вы уверенно себя чувствуете"

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

    А зазубренное знание API и кишочков конкретного фреймворка - очень ограничивает интервьювера в объективности оценки да и в понимании кандидата в целом.


    1. Vlad_IT
      16.11.2021 15:07

      Принцип работы стоит знать, это же основной инструмент. Я много раз видел, как в корневом компоненте в useSelector возвращают объект, который не меморизируется, из-за этого обновляется все дерево компонентов, это создаёт тормоза на больших приложениях.

      Но нужно ли такое задавать на собеседовании, тоже не уверен. Разве что в контексте "проведите ревью кода", и в одну из проблем вставить эту. Тот, кто сталкивался/знает, сразу обратит внимание.


      1. shybovycha
        16.11.2021 15:15
        +3

        Каждая команда нуждается в разных специалистах, но обратите внимание на это:

        Тот, кто сталкивался/знает, сразу обратит внимание.

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


        1. Alexandroppolus
          16.11.2021 16:03

          если человек найдется, но окажется крайне специфичным

          Пример с созданным на лету и незамемоизеным объектом абсолютно типовой (как для реакта, так и для js в целом), и это может заметить даже чел, который новичок в реакте, но толковый и шарит в js.


        1. faiwer
          16.11.2021 16:39

          сталкивались ли вы с вот такой проблемой

          Если человек долго писал на redux, то точно сталкивался и запомнил до конца жизни. Это не мелочь, а один из ключевых моментов технологии. Ну либо это свидетельство того, что человек вообще не вникает в то, чем он занимается. Такой человек будет головной болью.


          Просто интервьювер должен заранее выяснить, область компетенции кандидата и задавать ему соответствующие вопросы. Например, если человек долго пишет на MobX или ​Vue, я спрашиваю:


          • Опишите, пожалуйста, в двух словах как работает паттерн observable

          Если он даёт хороший ответ, я спрошу:


          • А как бы вы реализовали механизм tracking dependencies ​(в двух словах)

          Если отвечает на оба, то видно, что с таким человеком можно сварить кашу. Можно уйти в отпуск и оставить его за главного. Если же нет, то пристальное внимание на каждом MR.


          Если есть тестовое задание, то лучше оставить выбор технологии на выбор кандидата. Мне присылали решения, где человек руками писал EventEmmiter и строил вокруг него свой микро-фреймворк. Не трудно догадаться, что к такому кандидату особо пристальный интерес.


          А если человек со всем работал, ничего толком не помнит, нигде ни во что не вникал, умеет строить простые формочки на чём угодно… А последние два года писал micro-services с "кафкой", так что даже синтаксис JS\TS забыл и цикл написать не может… То может быть чувак и хорош, но это как минное поле.


          1. Kwisatz
            17.11.2021 21:38
            +2

            Смотрю я на ваши вопросы, толи у вас бюджет для найма космический, толи я в каком то ином мире живу, толи я вопросы ваши не понимаю, потому что 99 из 100 кандидатов до таких материй как до китая.

            А можно ваш ответ на оба этих вопроса (в двух словах)?


            1. faiwer
              17.11.2021 21:53

              А можно ваш ответ на оба этих вопроса (в двух словах)?

              Простейшая имплементация:


              • пишем класс
              • у класс есть массив: subscriptions: (() => void)[]
              • есть значение value: T
              • есть метод задающий значение setValue(newValue: T): void
              • есть метод для получения текущего значения getValue(): T
              • есть метод для подписки: subscribe(fn: () => void): void
              • при подписке fn заносится в subscriptions
              • при установке значения оно записывается в this.value и все this.subscriptions вызываются (уведомляются об изменении значения)
              • ещё можно отписаться
              • всё

              Эту простую схему можно многократно усложнить. И добавить сахару. Например посредством proxy, или get-set-ов. Или как в Knockout-е за счёт того что сам observer это метод. Но это уже не основы, а немного дальше.


              По 2-му вопросу — в самом простом случае имеем глобальный массив, куда записываем все затронутые чтения observables. По окончанию вычисления computed-метода просто автоматически на них подписываемся. Ну и не забываем отписаться от предыдущих лишних observables, т.к. их число могло измениться. В самом упрощённом виде он так и работает.


          1. strannik_k
            17.11.2021 22:13
            +1

            Мне присылали решения, где человек руками писал EventEmmiter и строил вокруг него свой микро-фреймворк. Не трудно догадаться, что к такому кандидату особо пристальный интерес.

            Я когда-то писал стейт-менеджер и успешно использовал его в парочке проектов, да и другие вещи писал. Но вот в тестовом не стал бы использовать (хотя сейчас и на тестовое не стал бы тратить время). В мире, в котором я живу, использование самописных решений вместо популярных готовых считается признаком неопытности.


            1. markelov69
              17.11.2021 22:32
              +1

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

              Это потому что уровень подавляющего числа разработчиков низкий и крайне низкий, поэтому если вы среди них выделяетесь, то вы для них белая ворона которая сошла с ума. Но на самом деле проблема не в вас, а в них) Не стоит падать до их уровня и ставить крест на том, где у вас есть реальная предрасположенность и вы одарены, а для них это лишь промысел за который они получают ЗП выше чем на кассе в магазине. Если вы считаете что ваш подход, ваше решение, ваша архитектура лучше чем то, что сейчас в основном применяется, то это нормально и 99% ваше версия и правда будет лучше, т.к. всё общепринятое обычно рассчитано на, что по максимуму загнать в угол джунов и не давать сделать шаг влево/вправо.


            1. faiwer
              18.11.2021 00:46

              использование самописных решений вместо популярных готовых считается признаком неопытности

              На самом деле это неплохой фильтр со стороны кандидата. Если интервьювер на полном серьёзе гонит на то, что вы вместо того, чтобы задействовать условный Vue, реализовали его сами, то это отличный повод им не перезвонить ;-)


      1. markelov69
        16.11.2021 15:57
        +6

        это же основной инструмент

        Что? Основной инструмент? Redux давно умер, по инерции до сих пор кто-то его юзает конечно из-за незнания альтернатив. А все нормальные люди уже много лет используют MobX.


        1. Sin9k Автор
          16.11.2021 16:04
          +1

          А все нормальные люди уже много лет используют MobX.

          На прошлом проекте 1.5 года использовали mobX, не понравилось)
          И все новые вакансии, почему просят Redux. Вероятно не так уж и умер)
          А в комментариях под моими видео, уже фанаты Effector во всю просят сделать видосы на эту тему. Так что даже и не знаю, что будет в будущем


          1. copperfox777
            16.11.2021 18:10
            +1

            Сбер, Яндекс, Ржд, ещё несколько крупных банков. Везде redux, часто с тулкитом или рематчем.

            Проста, удобство и предсказуемость


          1. Alexandroppolus
            17.11.2021 13:44

            1.5 года использовали mobX, не понравилось)

            Почему не понравилось? Субъективно, или были какие-то проблемы с мобиксом в конкретных случаях? Не холивара ради, а просто интересно )


            1. Sin9k Автор
              17.11.2021 13:54

              Я думаю, об этом в будущем написать. Там много мелких моментов:
              - возникла проблема потери контроля над рендерингом. Там observer управляет обновлять компонент или нет. В итоге при обновлении версии mobX. У нас приложение не запустилось. Т.к. порядок рендеринга компонентов изменился и наш код к такому не готов. Это было очень стремно. Мы не знали где еще отломится.
              - нет best practice. Ребята из командыы начали активно продвигать идею использовать активно подписки, чтобы реагировать на все. В итоге, опять вышла потеря контроля, над тем в каком порядке код выполняется. И зачастую выполняется лишний код, который в такой ситуации не нужно реагировать.
              - идеалогия не декомпозировать стор, а прокидывать весь стор в компоненты, так же накладывает сложности
              - опять же отсутствие бест практис. И предыдущая команда в сторах хранила не только данные а и всю бизнес логику. Приложение было очень связанным и в итоге сторы уже включали часть логики других сторов.
              - с памятью проблемы начались. То инстансы где то в реакциях зависали, то реакцию повесить можно внутри например User. И если юзеров кол-во растест сильно, то реакции тоже сильно растут и мы сравнивали сколько памяти это съедает


              1. Alexandroppolus
                17.11.2021 14:10

                Я думаю, об этом в будущем написать.

                Интересно будет почитать. В идеале - с компактными примерами, иллюстрирующими суть проблемы.


              1. markelov69
                17.11.2021 16:24
                -3

                Все что вы описали это проблемы чисто в вас и вашей команде как разработчиков, не достаточно опыта, не достаточно квалификации и т.п. Ни одной проблемы связанной с MobX'ом нету, только проблемы вытекшие из вашего кода и подхода. Если не думать головой, то можно и забивая гвоздь в доску пробить себе голову)


                1. Sin9k Автор
                  17.11.2021 17:08
                  +1

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


              1. strannik_k
                17.11.2021 21:43

                И предыдущая команда в сторах хранила не только данные а и всю бизнес логику. Приложение было очень связанным и в итоге сторы уже включали часть логики других сторов.

                Почему-то не получилось добавить коммент к вашему видео. В общем, если интересно, можете взглянуть на пару последних моих статей на хабре. Использую небольшие сторы только для данных и работой с этими данными (чтение/запись). Поток данных как в redux, но нет этих избыточных actions и dispatch. Пришел к такой архитектуре до того, как стал использовать Mobx, но она хорошо применима к нему и не только.

                Ну а так, согласен, что из-за отсутствия best practices у большинства получаются раздутые сторы с хаотичными связями и потоком данных.

                Ребята из команды начали активно продвигать идею использовать активно подписки, чтобы реагировать на все. В итоге, опять вышла потеря контроля, над тем в каком порядке код выполняется. И зачастую выполняется лишний код, который в такой ситуации не нужно реагировать.

                Не понял про идею, но по-моему, сейчас это стандартная ситуация практически на любом проекте с хуками) Обязательно в проекте присутствуют компоненты с несколькими хуками, у которых по несколько переменных в массиве зависимостей. И получается, что компонент зависит от нескольких десятков переменных. Ну и обычное дело, когда при вводе в input обновляется вся форма.


        1. faiwer
          16.11.2021 16:42

          Это случаем не вы https://habr.com/en/users/MaZaAa/? :)


    1. faiwer
      16.11.2021 16:30

      А зазубренное знание API и кишочков конкретного фреймворка

      Я думаю это вопрос для тех, кто "да я на redux собаку съел". От такого человека ожидаешь понимания, используемой им технологии. Если же человек, претендующий на архитекторскую позицию, говорит, что годами пишет на redux, но не знает таких элементарных вещей (которые описаны в этой статье), то он или вам нагло врёт, или его познания ниже плинтуса. Вы не можете писать на redux хоть сколько-нибудь адекватный код не зная этих вещей.


      Т.е. речь идёт не о знании кишочков конкретного фреймворка, а проверка на общую адекватность человека, которые заявляет что использует redux в своей практике широко.


      Можно ответить:


      • я плохо помню, давно писал на redux, с тех пор столько воды утекло

      И тогда к нему никаких претензий. Но такого человека наверняка спросят:


      • скажите, а с какой технологией вы работали наиболее плотно?
      • гхм… rxJS
      • хардкорный вопрос по rxJS


      1. Alexandroppolus
        16.11.2021 16:35

        хардкорный вопрос по rxJS

        Это если собеседующие шарят в rxJS. Так что, тут как повезет.


      1. shybovycha
        17.11.2021 00:08
        +4

        Если же человек, претендующий на архитекторскую позицию, говорит, что годами пишет на redux, но не знает таких элементарных вещей (которые описаны в этой статье),

        У каждого своя шкало и матрица компетенций, но по-моему, архитекторы впринципе не должны отвечать на вопросы по параметрам функций конкретного фреймворка - они должны знать преимущества и недостатки такого фреймворка максимум. Решение задач "как написать код оптимально" и "как удовлетворить документированные и недокументированные запросы клиента при этом не выкопав себе яму на будущее" - разного уровня компетенции. И знания тут нужно оценивать разные. Ну да я отвлекся.

        В остальном - да, именно то что вы написали - если человек заявляет что лучше всего владеет redux, то имеет смысл. Но опять-таки, смотря на каком уровне - сениора я бы больше проверял на решение сложных задач и широту познаний. Грубо говоря "не писал, но щас разберемся" - приемлемый ответ.

        хардкорный вопрос по rxJS

        Опять же, хардкорные вопросы на знание конкретных инструментов позволят узнать сколько в последнее время кандидат _писал код_ с использованием данного инструмента. Часто сениоры не столько пишут код, сколько планируют проект, обсуждают архитектуру решения, интервьювят, лидят пару человек на небольших проектах, менторят людей и решают сложные проблемы вида "вот у нас перформанс резко падает и никто не знает как оно воспроизводится". Но опять же, у каждого свое видение сениора.


        1. faiwer
          17.11.2021 00:35
          +2

          сениора я бы больше проверял на решение сложных задач и широту познаний

          По сути вопрос в статье как раз из той самой широты познаний. Просто оформлен по тупому (заход через аргумент функции). В react и redux балом правит иммутабельность. На её основе делается мемоизация, что является в них краегоульным камнем производительности и построения архитектуры.


          Вот вы пишете:


          они должны знать преимущества и недостатки такого фреймворка максимум

          Не зная всего того ^ человек не сможет рассуждать о преимуществах и недостатках. Откуда ему знать о проблеме O(n) подписчиков в redux, чтобы не взять его в проект, если он не понимает, как в общих чертах, работает redux. Откуда ему знать о паттернах проектирования React приложения, если он не знает как работает context в React (а к нему те же вопросы). Как человек сможет выстроить архитектуру на mobX или Vue, если observables для него магия.


          Т.е. это не вопрос вида: каков 2-й аргумент в parseInt и для чего он нужен? А скорее: что такое индексы в РСУБД и как ими пользоваться? Просто автор вместо прямого вопроса в лоб, решил зайти издалека, взяв конкретную функцию и контр-примеры к ней.


          решают сложные проблемы вида "вот у нас перформанс резко падает и никто не знает как оно воспроизводится"

          Не может человек решать такие вопросы не зная основ того, чем он пользуется.


          1. Feeritovec
            17.11.2021 09:49
            -2

            Не может человек решать такие вопросы не зная основ того, чем он пользуется.

            Могут и решают. Локализация проблемы перформанса никак не зависит от основ.


            1. faiwer
              17.11.2021 11:31
              +3

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


              Вообще я очень удивлён увидеть на хабре позицию, что не нужно знать как работают твои инструменты.


              1. Feeritovec
                17.11.2021 13:19

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

                Не может человек решать такие вопросы не зная основ того, чем он пользуется.

                Я видел, как решаются такие проблемы без понимания глубоких основ- через поиск боттлнека (дебажа библиотеки и понимая смысл) и гугления возможных проблем. Уже после этого человек приобретал более глубокое знание.

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

                Вообще я очень удивлён увидеть на хабре позицию, что не нужно знать как работают твои инструменты.

                Я этого не писал, вы за меня что-то придумали, но этому я не удивляюсь. Моя позиция в том, что для среднестатистического мидла/сеньора не обязательно знать досконально на 100% свою технологию, как выше уже писали "кишки", что бы выполнять свои задачи.


                1. Sin9k Автор
                  17.11.2021 13:39
                  +2

                  Моя позиция в том, что для среднестатистического мидла/сеньора не обязательно знать досконально на 100% свою технологию, как выше уже писали "кишки", что бы выполнять свои задачи.

                  Мне кажется вы делаете подмену понятий. "знание основ" и "знать досконально на 100% свою технологию". Это две разные вещи.

                  Если привести аналог на языке JS, я бы сравнивал свой вопрос, как "Как работает всплытие событий?". Нам так или иначе приходится с этим взаимодействовать. Без глубоких знаний можно ли решить эту проблему. Конечно можно. Загуглить на stackoverflow что-то, вставить и вуаля заработало! Нужен ли мне на позиции Senior JS developer, такой человек. Мои проекты чаще всего очень сложные и такие люди без основ будут отнимать у меня много времени.

                  А если говорить, про 100% знания языка. Я мог бы спросить про то как работают генераторы в JS. Насовать кучу примеров. Я ведь этого не делаю. Я ожидаю от кандидата, что бы он умел решать базовые задачи уверенно, а не кое как с гуглом и вопросами справился. А потом еще на ревью 3 раунда переделывал


                  1. faiwer
                    17.11.2021 14:14

                    Я мог бы спросить про то как работают генераторы в JS.

                    У меня был случай, когда коллега для того чтобы задать 3 разных classNames в props, исходя из 3 разных boolean флагов, написал генератор. А потом ещё тесты на генератор.


                    Т.е. условный:


                    [a && 'a', b && 'b', c && 'c'].filter(Boolean).join(' ');

                    превратился в 70+ строк кода в двух файлах :)


                    1. Sin9k Автор
                      17.11.2021 14:17

                      жуть какая)


                    1. Alexandroppolus
                      17.11.2021 14:44

                      Скорее всего, чувак недавно открыл для себя генераторы, и хотел где-то применить. Плюс наверняка ещё зеленый, не выгоревший - такие любят всё хайповое и модное, не умеют критически смотреть на инструменты.


                  1. strannik_k
                    17.11.2021 22:29

                    Мои проекты чаще всего очень сложные и такие люди без основ будут отнимать у меня много времени.

                    Мне было бы интересно услышать о ваших проектах и/или в чем их сложность? Как и где искать такие проекты и как туда попасть?) Я понимаю, что есть сложные проекты, но я за почти 10 лет работы веб-разработчиком только один свой проект могу назвать сложным. В других если и была сложность, то только по причине большего количества спагетти кода на момент моего прихода. Собеседования были гораздо сложнее, чем сами проекты.


                    1. Sin9k Автор
                      17.11.2021 23:03

                      Если брать из последних проектов то было следующее:
                      - мессенджер (конкурент телеграмам вайберам и т.д.). Это один из самых сложных для фронтендера проектов в принципе
                      - проект состоящих из двух проектов. При том каждый проект как в браузере так и на React Native. И нужно было выделить пере используемые части в lerna. Что-то пере используется между браузером и мобилкой. Что-то между двумя RN проектами. Что-то между двумя браузерными проектами. Я активно принимал участие в проектировании этой архитектуры.
                      - создание платфомы кодовой базы, через lerna. У продукта есть запрос, выпускать десятки похожих проектов, с немного отличающейся бизнес логикой. Поэтому нужно создать своеобразный конструктор.

                      Как их искать. Ну... я при поиске всегда обсуждаю, чтобы проект был хардовый. В итоге соглашаюсь, только на те проекты, где мне кажется, что будет очень увлекательно девелопить


                      1. strannik_k
                        18.11.2021 21:35

                        Спасибо!

                        Месседжер - довольно интересно.

                        А вот с lerna не знаком, поэтому сложно представить. Конструктор для этого - имеется ввиду на формах сделанный?


                      1. Sin9k Автор
                        18.11.2021 21:44
                        +1

                        Нет. Это значит весь твой проект состоит из npm пакетов. Представь, что у тебя есть форма регистрации. Ты ее берешь и выносишь в npm пакет. Потом ты можешь подключить форму регистрации в несколько проектов твоей компании, т.к. всем проектам нужна одинаковая регистрация. Таких npm пакетов у тебя становится штук 20-30. Управлять ими по отдельности сложновато. Поэтому придумали инструмент lerna, который позволяет управляться с этими npm пакетами проще и быстрее.

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


                    1. faiwer
                      18.11.2021 00:55

                      Просто выбирайте компании, которые занимаются чем-нибудь амбициозным. Например напроситесь в 2gis. Это картография, она всегда сложная. Или наймитесь писать какую-нибудь соц. сетку. Это тоже большой пласт интересных знаний. Или какой-нибудь GUI редактор, например web-версия условного Word/Excel-я. Найдите компанию, которая что-нибудь архитектурное (всякие 3д редакторы) пишет.


                      На самом деле не понятно, как люди умудряются избегать интересных задач. У меня их была уйма. И очень интересный иерархические virtual-scroll, и объектные визивиг-редакторы, и соц. сеть, и редактор уроков для школьников, и UI штука для составления расписаний занятий и пр… Оно как-то само находит тебя, когда ты готов заниматься чем-нибудь интересным. Обычно это многолетние проекты с большими кодовыми базами и сложной предметной областью.


                      Если же верстать лендинги целыми днями, то можно ж со скуки умереть :)


                      1. strannik_k
                        18.11.2021 22:16

                        Спасибо за совет! Вот некий редактор и был единственным интересным веб проектом. Жаль, что там был слишком плохой код и я не стал там задерживаться. А так, больше половины моих проектов - админки.

                        Тут еще момент, что с React почти везде используется Redux (в 2gis тоже) со своим зоопарком технологий, а мне он никогда не нравился. Не горю желанием тратить время на технологию, которая мне не интересна, диктует мне свою архитектуру, и без которой получается делать проще и быстрее.


                    1. faiwer
                      18.11.2021 00:59

                      то только по причине большего количества спагетти кода на момент моего прихода

                      Если ваша роль при этом заключается в том, чтобы всё исправить, то это может быть весьма увлекательным. У меня был один такой проект. Это был качественный скачок в развитии архитекторских навыков. Одно дело знать грабли на которые не стоит наступать, и не наступать на них самому. И другое дело вкусить наполную уже результат наступания (причём не наступая самому), и научиться всё это дело исправлять. Это такой интересный урок. Теоретические познания мгновенно отражаются на пратике. Ты знаешь, что подход Х, тут исправит такие-то проблемы. Ты его внедряешь, проблемы исправляются, ты в первых рядах — понимаешь всё в деталях. Прямо подлинное понимание приходит. И соответственно крепкий навык. Когда в голове что-то из категории "догма" переходит в категорию "полезный инструмент".


                      Но если лечить проект не позволяют и заставляют писать всё ту же лапшу, то лучше сразу свалить.


                      1. strannik_k
                        18.11.2021 22:26

                        Такой роли у меня не было. Бывало пару раз, что на масштабный рефакторинг выделяли месяц-два.

                        Архитектурные навыки я неплохо прокачал другим способом. И итак нарефачился за свою жизнь) В одном проекте кто-то ключевой функционал сделал одной функцией на 800+ строк, плюс хватало проблем и в других местах. В другом - реакт с ангуляром вместе использовался и потом это переписывали на es6 и попутно избавлялись от angular. В третьем - с jquery на react переписывали.


                    1. avengerweb
                      18.11.2021 06:26

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


                1. faiwer
                  17.11.2021 14:02
                  +1

                  Уже после этого человек приобретал более глубокое знание.

                  Ну т.е. не зная основ, человек должен приобретать эти основы. И вот тут всё упирается в то, насколько легко понять основы. Чем менее прямолинейная логика, тем сложнее.


                  Вот скажем дебажить React будет "одно удовольствие", т.к. там Fiber-ы с дроблённым рендером и несколькими фазами. Можно утонуть просто по уши. И профилировать такое сложно, т.к. оно реально дробится на чанки длинной не более кадра. Простые механизмы не работают. КПД такого дебага будет очень низким.


                  В случае MobX, Knockout, Vue свои приколы. Скажем дебаг computedObservables из нокаута, когда они там разного вида (асинхронные, debounced, синхронные), да ещё и с массивами — то получается такая лютая каша, что даже зная, как это всё работает, часы дебага могут привести ни к чему. А учитывая что там ну ничего не детерминировано, то прямо печаль. Зубами приходится впиваться в каждый кейс, боясь перезагрузить вкладку.


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


                  Про таких людей я и писал выше, что им нужно задавать вопросы не абы какие, а связанные с теми технологиями, с которыми они очень плотно работали. Тогда и легко становится понять, на что способен человек. Не приходится гадать. Человек съевший собаку на технологии Х, может вам часами про неё говорить.


                  что для среднестатистического мидла/сеньора не обязательно знать досконально на 100% свою технологию

                  Зачем здесь слово досконально? Мы ведь говорим про основы. Основы это не досконально. Это… основы. Азы.


                  Уточню — эта статья описывает азы.


                  1. Feeritovec
                    17.11.2021 14:31
                    +1

                    Согласен. Видимо я стригеррился на категоричность фразы. Удачи и хороших вам кандидатов!


    1. SergeyMax
      16.11.2021 23:20
      +3

      Считаю что на подобные вопросы кандидаты уровня мида и выше могут с уверенностью отвечать "не помню, щас загуглю".

      Считаю, что синьоры на подобные вопросы могут с уверенностью отвечать встречным вопросом "а какой третий параметр принимает функция имя_функции", чтобы спрашивающий уже сам начал бормотать про гугл)


      1. shybovycha
        17.11.2021 00:13
        +1

        Думал написать в стиле "сениоры и выше могут смело закончить интервью"


    1. evgeniyPP
      18.11.2021 19:18

      Задавать вопросы, на которые легко отвечает любое IDE, — это та ещё пустая трата времени.


  1. faiwer
    16.11.2021 16:27
    +1

    Так делать не рекомендуется

    А почему просто "не рекомендуется", вместо "за это бьют ногами"?
    P.S. обсуждаемая тема хорошо описана в первых подкастах "Пятиминутка React".


  1. JSmitty
    16.11.2021 17:49
    +1

    Не увидел главного тезиса - "вызов dispatch - потенциально очень дорогой, чем больше компонентов с редаксом (connect/useSelector) - тем дороже dispatch". Начиная с какого-то уровня, мемоизация тоже не очень спасает (особенно то, что есть в createSelector).


  1. Kasheftin
    16.11.2021 19:57
    +1

    Сам давно мигрировал на vue+vuex, но там та же ситуация. Построение зависимостей от объектов, а объекты создаются налету (через {...}), или новые массивы создаются налету через .filter, и все computed начинают перестраиваться чаще, чем требуется. Я про это тоже писал в разрезе vue, https://habr.com/ru/post/587946/, https://habr.com/ru/post/543298/.


  1. stardust_kid
    17.11.2021 19:32
    +3

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

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


  1. JordanoBruno
    17.11.2021 20:19
    +4

    Я обычно спрашивал: “Какой первый параметр принимает connect?”

    Бессмысленные и беспощадные собеседования. Еще бы спросил - на какой странице и какой строке в оф. доке это написано?


  1. DaturInnoxia
    17.11.2021 21:31
    +1

    Прекрасный и "простой" React не такой уж простой и там есть подводные камни?


  1. PyVolshebnyi
    18.11.2021 03:19
    +1

    А будет считаться правильным ответом, что в redux вообще нет функций `mapStateToProps` и `connect`? Это явно про какую-то другую библиотеку.


    1. Sin9k Автор
      18.11.2021 11:01
      +1

      Однозначно будет :)


  1. avengerweb
    18.11.2021 06:15
    -2

    Я конечно не реакт профи, но на моменте о 3 независимых компонентах, и глобальном стейте из которого они тащат данные я бы закрыл окошко с зумом. Если хотите сэкономить время на интервью, можете дать HR ссылку на документацию редакса, пусть он/она выбирает из неё рандомным функцию и спрашивает сколько там параметров ???? сразу половину кандидатов отсеете, удобно!


    1. Sin9k Автор
      18.11.2021 11:07

      А что вы найдете в документации? как работают 3 независимых компонента?

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


      1. stardust_kid
        18.11.2021 21:08

        Ну-ну, расскажите нам, как только в вашей компании программисты понимают Redux и яваскрипт, а остальные разработчики так, кривляются. И вообще, вас, наверное, Дэн Абрамов зовут?

        Я на собеседовании ожидаю адекватных вопросов, интересных мне и способных показать мой профессиональный уровень. Когда наобум спрашивают про любимую апишку интервьюера, это не профессионализм, а попытка поиграть комплексами на моем фоне. Причем за бесплатно. Когда был джуниором, расстраивался; сейчас как @avengerweb дропаю во время звонка, после вежливого прощания. Пускай к ним Дэн Абрамов и Кент Си Доддс обращаются за работой.


        1. Alexandroppolus
          18.11.2021 23:06

          Вопрос о 3 независимых компонентах - это совсем не про апишку, а именно о принципе работы инструмента (если дать развернутый ответ).

          Вот, к примеру, просить подробно рассказать про четвертый параметр функции connect действительно было бы неуместным задротством, потому что эти знания сиюминутно под рукой, и их не надо помнить наизусть. Или, допустим, спрашивать какие-нибудь малоизвестные синтаксические тонкости. Это всё хрень, которую не отходя от кассы подскажет ВебШторм или этот ваш ВСКод.

          Вопросы на понимание сути, 1-2 задачки на умение кодить, и что-нибудь про архитектуру/декомпозицию/чистый_код - вот золотая триада хорошего собеса. Всё в меру, конечно.


          1. avengerweb
            19.11.2021 10:16

            Ну дак вы спрашиваете у человека вопросы про стейт менеджмент, и ваши вопросы все вокруг того что у вас есть 3 независимых компонента, которые почему то шарят один глобальный стейт между собой. Конечно важно знать как обойти проблему с тем что у вас все компоненты обновляются вместо одного. Чего этого можно было избежать например не имея глобальный стейт, там где он не нужен. Мой поинт в том что автор преподносит его ответы как единственно верные и вопросы важными.


            1. Sin9k Автор
              19.11.2021 12:05

              Мой поинт в том что автор преподносит его ответы как единственно верные и вопросы важными.

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