React hooks — победа или поражение?

    image


    С выходом нового React 16.6.0 в документации появился HOOKS (PROPOSAL). Они сейчас доступны в react 17.0.0-alpha и обсуждаются в открытом RFC: React Hooks. Давайте разберемся что это такое и зачем это нужно под катом.


    Да это RFC и вы можете повлиять на конечную реализацию обсуждая с создателями react почему они выбрали тот или иной подход.


    Давайте взглянем на то как выглядит стандартный хук:


    import { useState } from 'react';
    
    function Example() {
      // Declare a new state variable, which we'll call "count"
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }

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


    Как уверяют ребята, это не план по выпиливанию классов из реакта.


    Так же хуки не заменяют текущие концепции реакта, все на месте props/state/context/refs. Это всего лишь еще один способ использовать их силу.


    Мотивация


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


    Самое сложное это переиспользовать логику в stateful компонентах, у реакта нет способа прикрепить многоразовое поведение к компоненту(например подключить его к хранилищу). Если вы работали с React вам известно понятие HOC(high-order-component) или render props. Это достаточно хорошие паттерны, но иногда они используются чрезмерно, они требуют реструктуризации компонентов, для того, чтобы их можно было использовать, что обычно делает код более громоздким. Стоит посмотреть на типичное реакт приложение и станет понятно о чем идет речь.


    image


    Это называется wrapped-hell — ад оберток.


    Приложение из одних HOC это нормально в текущих реалиях, подключили компонент к стору/теме/локализации/кастомным хокам, я думаю это всем знакомо.


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


    С помощью хуков мы можем извлекать состояние компонента, так чтобы его можно было тестировать и переиспользовать. Хуки позволяют повторно использовать логику состояния без изменения иерархии компонентов. Это облегчает обмен ссылками между многими компонентами или всей системы в целом. Так же классовые компоненты выглядят достаточно страшно, мы описываем методы жизненного цикла componentDidMount/shouldComponentUpdate/componentDidUpdate, состояние компонента, создаем методы для работы с состоянием/стором, биндим методы для экземпляра компонента и так можно продолжать до бесконечности. Обычно такие компоненты выходят за рамки x строк, где x достаточно сложно для понимания.


    Хуки позволяют делать тоже самое разбивая логику между компонентами на маленькие функции и использовать их внутри компонентов.


    Классы сложны для людей и для машин


    В наблюдении facebook классы являются большим препятствием при изучении React. Вам необходимо понять как работает this, а он не работает так как в остальных языках программирования, так же следует помнить о привязке обработчиков событий. Без стабильных предложений синтаксиса код выглядит очень многословно. Люди прекрасно понимают паттерны props/state и так называемый top-down data flow, но достаточно сложно понимают классы.


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


    Глянем на хуки


    State hook


    Код ниже рендерит параграф и кнопку и если мы нажмем на кнопку то значение в параграфе будет инкрементировано.


    import { useState } from 'react';
    
    function Example() {
      // Declare a new state variable, which we'll call "count"
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }

    Отсюда можно сделать вывод что данный хук работает похоже с таким понятием как state.
    Немного детальнее метод useState принимает один аргумент, это значение по умолчанию и возвращает кортеж(tuple) в котором есть само значение и метод для его изменения, в отличие от setState, setCount не будет делать merge значений, а просто обновит его. Так же мы можем использовать множественное объявление состояний, например:


    function ExampleWithManyStates() {
      // Declare multiple state variables!
      const [age, setAge] = useState(42);
      const [fruit, setFruit] = useState('banana');
      const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
      // ...
    }

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


    Effect hook


    Часто в классовых компонентах, мы делаем side effect функции, например подписываемся на события или делаем запросы за данными, обычно для этого мы используем методы componentDidMount/componentDidUpdate


    import { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      // Similar to componentDidMount and componentDidUpdate:
      useEffect(() => {
        // Update the document title using the browser API
        document.title = `You clicked ${count} times`;
      });
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>
      );
    }

    Когда мы вызываем useEffect мы говорим реакту сделать 'side effect' после обновления изменений в DOM дереве. Эффекты объявляются внутри компонента, поэтому имеют доступ к props/state. Причем их мы можем точно так же создавать сколько угодно.


    function FriendStatusWithCounter(props) {
      const [count, setCount] = useState(0);
      useEffect(() => {
        document.title = `You clicked ${count} times`;
      });
    
      const [isOnline, setIsOnline] = useState(null);
      useEffect(() => {
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        return () => {
          ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
      });
    
      function handleStatusChange(status) {
        setIsOnline(status.isOnline);
      }
      // ...

    Сразу же стоит обратить внимание на второй side effect в нем мы возвращаем функцию, делаем мы это для того, чтобы выполнить какие то действия после того как компонент выполняет unmount, в новом api это называют эффекты с очисткой. Остальные эффекты могут возвращать, что угодно.


    Правила хуков


    Хуки это просто javascript функции, но они требуют всего двух правил:


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

    Кастомные хуки


    В тоже время нам хочется переиспользовать логику stateful компонентов, обычно для этого используют либо HOC либо render props паттерны, но они создают дополнительный объем нашего приложения.
    Например опишем следующую функцию:


    import { useState, useEffect } from 'react';
    
    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);
    
      function handleStatusChange(status) {
        setIsOnline(status.isOnline);
      }
    
      useEffect(() => {
        ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
        return () => {
          ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
        };
      });
    
      return isOnline;
    }

    Осознайте этот код, это будет кастомный хук, который мы можем вызывать в различных компонентах. Например так:


    function FriendStatus(props) {
      const isOnline = useFriendStatus(props.friend.id);
    
      if (isOnline === null) {
        return 'Loading...';
      }
      return isOnline ? 'Online' : 'Offline';
    }

    или так


    function FriendListItem(props) {
      const isOnline = useFriendStatus(props.friend.id);
    
      return (
        <li style={{ color: isOnline ? 'green' : 'black' }}>
          {props.friend.name}
        </li>
      );
    }

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


    Есть еще пара хуков.


    useContext


    useContext позволяет использовать вместо renderProps обычное возвращаемое значение, в него следует передать контекст который мы хотим извлечь и он нам его вернет, таким образом мы можем избавиться от всех HOC, которые передавали context в props.


    function Example() {
      const locale = useContext(LocaleContext);
      const theme = useContext(ThemeContext);
      // ...
    }

    И теперь объект контекста мы можем просто использовать в возвращаемом значении.


    useCallback


    const memoizedCallback = useCallback(
      () => {
        doSomething(a, b);
      },
      [a, b],
    );

    Как часто вам приходилось создавать компонент класса только для того, чтобы сохранить ссылку на метод? Этого больше не нужно делать, мы можем использовать useCallback и наши компоненты не будут перерисовываться потому что пришла новая ссылка на onClick.


    useMemo


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


    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

    Да тут приходится дублировать значения в массиве, чтобы хук понял, что они не изменились.


    useRef


    useRef возвращает мутируемое значение, где поле .current будет инициализировано первым аргументом, объект будет существовать пока существует компонент.
    Самый обычный пример при фокусе на input


    function TextInputWithFocusButton() {
      const inputEl = useRef(null);
      const onButtonClick = () => {
        // `current` points to the mounted text input element
        inputEl.current.focus();
      };
      return (
        <>
          <input ref={inputEl} type="text" />
          <button onClick={onButtonClick}>Focus the input</button>
        </>
      );
    }

    useImperativeMethods


    useImperativeMethods кастомизирует значение экземпляра который передается из родителя и использует ref напрямую. Как всегда следует избегать передачу ссылок на прямую и следует использовать forwardRef


    function FancyInput(props, ref) {
      const inputRef = useRef();
      useImperativeMethods(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        }
      }));
      return <input ref={inputRef} ... />;
    }
    FancyInput = forwardRef(FancyInput);

    В этом примере компонент который рендерит FancyInput может вызывать fancyInputRef.current.focus().


    useMutationEffect


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


    useLayoutEffect


    useLayoutEffect так же похож на useEffect за исключением того, что запускается синхронно после всех обновлений DOM и синхронного ре-рендера. Обновления запланированные в useLayoutEffect применяются синхронно, до того как браузер получит возможность отрисовать элементы. Так же следует стараться использовать стандартный useEffect чтобы не блокировать визуальные изменения.


    useReducer


    useReducer — это хук для создания редюсера который возвращает состояние и возможность диспатчить изменения:


    const [state, dispatch] = useReducer(reducer, initialState);

    Если вы понимаете как работает Redux, то вы понимаете как работает useReducer. Тот же пример, что был с счетчиком сверху только через useReducer:


    const initialState = {count: 0};
    
    function reducer(state, action) {
      switch (action.type) {
        case 'reset':
          return initialState;
        case 'increment':
          return {count: state.count + 1};
        case 'decrement':
          return {count: state.count - 1};
      }
    }
    
    function Counter({initialCount}) {
      const [state, dispatch] = useReducer(reducer, initialState);
      return (
        <>
          Count: {state.count}
          <button onClick={() => dispatch({type: 'reset'})}>
            Reset
          </button>
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
        </>
      );
    }

    Так же useReducer принимает 3 аргумент, это action который должен выполнятся при инициализации редюсера:


    const initialState = {count: 0};
    
    function reducer(state, action) {
      switch (action.type) {
        case 'reset':
          return {count: action.payload};
        case 'increment':
          return {count: state.count + 1};
        case 'decrement':
          return {count: state.count - 1};
      }
    }
    
    function Counter({initialCount}) {
      const [state, dispatch] = useReducer(
        reducer,
        initialState,
        {type: 'reset', payload: initialCount},
      );
    
      return (
        <>
          Count: {state.count}
          <button
            onClick={() => dispatch({type: 'reset', payload: initialCount})}>
            Reset
          </button>
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
        </>
      );
    }

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


    Подводя итог


    Хуки достаточно мощный подход по решению wrapper-hell и решают несколько проблем, но все их можно свети с одному определению передача ссылок. Уже сейчас начинают появляться сборники хуков по использованию или этот сборник. Более подробнее с хуками можно познакомиться в документации.

    Комментарии 93

      +12
      Я ещё раз внимательно перечитаю статью, когда выпью чашечку послеобеденного чаю. Пока хочу поделиться первым впечатлением: на вид адище какое-то, право слово.
        +2
        Очень верное впечатление, только выпить захотелось чего покрепче
          +1
          Перечитал и всё равно не понял, зачем это нужно. Наверное, это потому. что я слишком мало пользуюсь HoC и никогда не сталкивался с HoC hell. Если бы кто-то привёл мне:

          1. Пример вышеупомянутого ада в реалистичных условиях
          2. Как хуки от него спасают
          3. Почему от него не спасает классический подход

          — я был бы, во-первых, очень благодарен, а во-вторых, вероятно, изменил бы своё мнение относительно сабжа.
            0

            Это примерно вот так. Обратите внимание на нижнюю часть, где хлебные-крошки. Там есть скролл. Раньше эта панель показывалась без скролла и там было 3-5 строк одних только хлебных крошек. На производительность такая вложенность толком не влияет (она тут "затюнена" до упора). Всё "летает". Но вот отладка страдает, да. Просто тонны визуального мусора. Хотели добавить галочку "скрыть врапперы", и судя по скриншоту из статьи даже добавили, но у меня пока её нет.


            По большей части виной всему этому не HoC-hell, а context-hell. До React16 контекст не создавал новых звеньев в древе, после стал создавать сразу по многу. Частично я решил эту проблему декоратором, но только частично.

              0

              Ужас какой. Как вы это дебажите? А ведь могли же сделать хорошо.

                +3

                Обратите внимание на то, что мы сравниваем тёплое и красное. Я про древо компонентов React, а вы мне про итоговый HTML (а не про древо компонентов $mol). DOM то в итоге выглядит очень даже кратко и опрятно.


                Кстати, а эти жуткие ID-ики у каждой DOM-ноды и тысячи каких-то mol-аттрибутов — это обязательное следствие использования $mol? Эта стена мусора прямо аж обескураживает.

                  0
                  Это и дерево компонент тоже. В том-то и дело.

                  Айдишники — семантические хлебные крошки. Но нет, они не обязательны.
                    +3
                    Это и дерево компонент тоже. В том-то и дело.

                    • Т.е. каждый компонент завязан на одну и только одну DOM-ноду, да? Для сравнения в React компонент может быть завязан на 0+ DOM-нод. Это гораздо практичнее. Этого мне сильно не хватает во Vue.
                    • Нет возможности обернуть один компонент другим, не добавив лишний уровень DOM-иерархии. Или есть? Если есть, то утверждение что древо компонент = DOM-древу снова ложное.

                    Айдишники — семантические хлебные крошки

                    А зачем вам столько хлебных крошек? Зачем вообще эта гора мусора? Это не может не мешать дебагу.

                      0
                      Т.е. каждый компонент завязан на одну и только одну DOM-ноду, да?

                      Каждый компонент работает с одной дом нодой. Может со своей, может с чужой.


                      Для сравнения в React компонент может быть завязан на 0+ DOM-нод.

                      Это тоже можно, но нужно редко. В основном это плагины (Например, $mol_select подключает плагин $mol_nav для клавиатурной навигации). Ещё реже — обёртки (Например, $mol_book заворачивает все страницы в $mol_book_page чтобы примешивать к ним свои аттрибуты).


                      утверждение что древо компонент = DOM-древу снова ложное.

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


                      А зачем вам столько хлебных крошек?

                      1. Для аналитики: когда пользователь кликнул по такой-то кнопке — произошли такие-то изменения в модели.


                      2. Для тестирования: найти кнопку с таким-то семантическим (а значит стабильным) идентификатором и нажать на неё.


                      3. Для дебага: быстрый доступ через консоль к любому состоянию.


                      4. Для пререндеринга: можно взять хтмл с такими айдишниками, запустить приложение и оно при аттачится к соответствующим узлам.


                0
                Тоже очень расстроился, когда увидел сколько новое Context API генерирует мусора в дереве. А если наложить на это styled components с темами…

                Поделитесь опытом, как Вы с этим боретесь?
                  0

                  Очень просто — никак.

            +3
            Судя по стилю (отсутствию пунктуации и рассогласованности в некоторых местах), эта статья — перевод. Читать очень тяжело. Можете дать ссылку на оригинал?
              +3
              Оригинал это документация, плюс немного личного опыта.
              +8
              В наблюдении facebook классы являются большим препятствием при изучении React

              Прошу прощения, но неужели мир уже деградировал?
                0
                вероятно в данном высказывании имелось ввиду, что классы JS вводят в заблуждение, ибо это не то что в других ЯП.
                  0
                  Класс, это структура реализующая одновременно и состояние и поведение. Разве не так? Если так, то что из перечисленного классы в js делать не умеют?
                    0
                    Очень специфическая реализация классов в JS из которой сильно торчат ноги прототипного наследования. Ну и работа с this далеко не очевидна.
                  0
                  Вам необходимо понять как работает this, а он не работает так как в остальных языках программирования, так же следует помнить о привязке обработчиков событий.
                  Вы следующее предложение читали?
                    +1
                    Вам необходимо понять как работает this

                    А это такой тоже интересный момент. Мне кажется, что если в своих JS задачах вы достигли уровня, когда следует воспользоваться какой-либо библиотекой уровня React (т.е. задачи уже довольно серьёзные), то...


                    … то наверное знать язык, на котором вы пишете и правда имеет смысл. Нет? Я не прав?


                    Но реалии интереснее. В Slack-е webstandart-ов стал натыкаться на элементарнейшие вопросы в разделе #react. "У меня не работает, почему?". Лезу в код — о боже. Указываю на конкретную проблему… аргументирую… В итоге выясняю, что человек не понимает даже в каком порядке интерпретатор выполняет код. К примеру, что переменную стоило бы объявить и дать ей нужное значение ДО ТОГО, как пытаться что-нибудь из неё прочитать. Ну и прочая такая дичь.


                    Приходит человек собеседоваться. В резюме гордая строка "React-developer" (это вообще как? о_О). Проверки на знания языка неизменно приводят к ответу, что человек знает язык только на поверхностном уровне. React-а, разумеется, тоже не знает, но наколабасить что-то простое, что даже заведётся в состоянии. Наверное в этом и заключается гордое звание React-developer. Мне бы вот в жизни бы не пришла мысль называть себя в таком ключе. Backbone-developer? Lodash-developer? jQuery-developer? Mootools-developer? Vue-developer? Вы хто такие все? )


                    Но, видимо, это актуально. Актуально учить некий абстрактный React забив даже на JS.

                      +1
                      Но, видимо, это актуально. Актуально учить некий абстрактный React забив даже на JS.

                      Да, это та же JQ, только реактивная и от другого разработчика. История имеет свойство повторяться с завидной регулярностью.

                        0
                        А это такой тоже интересный момент. Мне кажется, что если в своих JS задачах вы достигли уровня, когда следует воспользоваться какой-либо библиотекой уровня React (т.е. задачи уже довольно серьёзные), то...
                        Увы, популярная практика «фуллстеков» приводит к тому, что человек знает только одну часть профессии нормально, но с другой части его спрашивают как будто он знает её нормально.

                        Но, видимо, это актуально. Актуально учить некий абстрактный React забив даже на JS.
                        Это верстальщики и свитчеры, не обращайте внимания.
                          0
                          В наблюдении facebook классы являются большим препятствием при изучении React.
                          Но реалии интереснее. В Slack-е webstandart-ов стал натыкаться на элементарнейшие вопросы в разделе #react. «У меня не работает, почему?». Лезу в код — о боже. Указываю на конкретную проблему… аргументирую… В итоге выясняю, что человек не понимает даже в каком порядке интерпретатор выполняет код. К примеру, что переменную стоило бы объявить и дать ей нужное значение ДО ТОГО, как пытаться что-нибудь из неё прочитать. Ну и прочая такая дичь.
                          Может все таки стоит признать что после наблюдений facebook и собственного опыта мир возможно реально уже деградировал? Судя по вашим результатам собеседования все действительно может быть плохо, и молодым новобранцам тяжело сразу вникнуть в суть. И поэтому идея с хуками имеет место быть так как это небольшой шаг назад чтобы сделать жизнь им (новобранцам, не вам специалистам с опытом) жизнь чуточку проще. Этакий функциональный подход.
                            +1
                            Это не функциональный подход, а магическо-DSL-подход. В ФП как бы функцию можно вызывать всегда, а не как тут — строго в начале метода и вне любых возможных условных операторов или циклов.
                            +1
                            … то наверное знать язык, на котором вы пишете и правда имеет смысл. Нет? Я не прав?

                            Тут дело не совсем в знать. Это как минимум неудобно и алогично, особенно в контексте других языков. Все эти «знать» находятся под и являются свойствами не классов, а того, что под них мимикрирует. И это «под» ортогонально базовым представления из тех же С++, java.

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

                            И зачем нам нужно то, что работает плохо и выглядит ужасно, если проще воспользоваться плюсами этой парадигмы и получит красивую, практичную и удобную магию. К тому же, этот мир(в том числе и реакт) пропитан тем, что не очень привычно людям из других миров. Кому и зачем в мире привычного ООП нужна чистота, иммутабельность и прочие?

                            Да и подобные решения не являются чем-то новым — это всё уже есть и используется в том же mobx и иже с ним. Магия является трендом и именно магия куда сложнее для понимания.
                              0

                              Ваш комментарий полон весьма субъективных оценок. Одна сторона у вас и работает плохо, и выглядит ужасно, и вообще непонятная\непривычная\кривая. Преимущества убоги и нефункциональны. Другая же сторона полна магии, которая в тренде, хоть и сложна для понимания, но выглядит красиво, и, вероятно, работает хорошо. Так?


                              Боюсь, что мы с вами одни и те же вещи оцениваем совсем по-разному. Очень по-разному. И я остаюсь при своём мнении, что даже если вы используете процедурный подход, знать как устроен this и prototype-chain в JS вам нужно, ибо это одна из центральных частей языка, от которой никуда не деться, и ни за какими React-ми не спрятаться.

                                +1
                                Одна сторона у вас и работает плохо, и выглядит ужасно, и вообще непонятная\непривычная\кривая.

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

                                Преимущества убоги и нефункциональны.

                                Преимущества есть, но они не здесь. Что, в контексте того же реакта, мне дают классы? Да ничего. Стейт прикручен сбоку, контекст тоже, пропсы тоже. Методы/поля не являются свойствами ТОЛЬКО класса, как в других языках.

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

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

                                Почти так, и тому есть причины. Зачем нужны тонны бойлерплейта и прочего ахтугда, если можно использовать красивую магию? Это безопасней, быстрее, удобнее. Да, сложнее.

                                Так?

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

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

                                Не использую.

                                знать как устроен this и prototype-chain в JS вам нужно, ибо это одна из центральных частей языка, от которой никуда не деться, и ни за какими React-ми не спрятаться.

                                Вот видите, в этом и проблема. Мне нужно понимать не классы, не их логику, а логику левых кишков. К тому же, эта логика куда мощнее классов(в контексте жс и его классов). Вот и получается, что у меня есть классы, которые целиком и полностью неюзабельны, а вся логика у меня находится сбоку. Вот и вопрос — зачем нужны такие классы?
                          0

                          В JS все нормально с классами. Просто реакт использует классы больше в качестве синтаксического сахара, чтобы меньше букв писать, нежели осмысленного ООП (они там больше от ФП фанатеют, судя по архитектуре). Рано или поздно подобные трюки приводят к замысловатому коду, странным плагинам для бабеля и умопомрачителным проблемам на ровном месте. Обработчики это классика местного фолклора.

                          +4
                          Пишут, что классы «сложны для понимания», но при этом добавляют ещё одну сущность. Не думаю, что эти прям таки хороший подход. Некоторые примеры, вообще не очевидными кажутся ( например, возвращаемая функция в useEffect )
                            +3
                            но при этом добавляют ещё одну сущность

                            Больше всего умиляет то, что для того чтобы избавиться от "сложных и непонятных классов", они добавили скрытый "трекинг" хуков (причём в последовательной манере). Это нехилая такая абстракция. На ровном месте. Чтобы не писать методов в классе. Хотели избавиться от this, и теперь пишем всё в одном замыкании. А для быстродействия надо методы оборачивать в useCallback-и (явно дублируя все зависимости). Ох, мои глаза. Зачем так то?


                            А патчем до этого они решили добавить context в старом стиле в классы. Но только для одного consumer-а. Какие-то странные ограничения на ровном месте. Зачем именно так?


                            React становится всё более и более… странным. Всё ещё понятным, но уже довольно странным. Даже вот redux встроили в react :) Сразу со switch-case-ми, dispatch-ми и экшнами. Эх.

                              0
                              Есть предложения лучше? Пролемы с трудностями понимания работы this есть точно, ровно как и проблемы с переиспользованием stateful кода.
                                0
                                Есть предложения лучше?

                                Нууу, например mixins & auto-bind в конструкторе Component. Чем не решение? На худой конец декоратор для методов класса.

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

                                  Миксинов тоже в JS нет, auto-bind с трудом представляю как можно реализовать.
                                    +1
                                    auto-bind с трудом представляю как можно реализовать.

                                    Например в constructor-е React.Component класса. Самое простое (чтобы без груды overhead-а) оборачивать все методы начинающиеся на on[A-Z]. Но можно и все вообще.


                                    но в JS декораторов нет и не понятно будут ли в обозримом будущем.

                                    Ну дык и JSX тоже нет в JS и не предвидится.


                                    Говоря про Mixin-ы, я имел ввиду что-то типа старого mixins: [mixin1, mixin2]. Всё равно пришли к тому же самому в итоге.

                                      +1

                                      Байндинг плохо уживается с наследованием. Вы не можете нормально использовать super в таких методах. На эти грабли наступают все, включая class props плагины для Babel.

                                        +1
                                        Байндинг плохо уживается с наследованием

                                        Насколько я могу судить не уживается с наследованием только class props. Любые другие решения, которые оставляют метод в прототипе, вроде мешать не должны, и super там работать должен.


                                        Ну и я имел ввиду конструктор именно React.Component класса, от него так и так классовые компоненты отнаследованы.

                                        0
                                        Не стоило им делать именно миксины, когда все смешивается в один объект.
                                        Мне вот нравиться подход в Unity3d. Там у объекта есть список компонентов (как и в react было (mixins: [mixin1, mixin2]). Вот только методы жизненного цикла объекта вызываются во всех компонентах из его списка. За счет этого нет проблем, как в случае с миксинами. Все отделено друг от друга.
                              0
                              Вообще жизнеспособно. Пока конечно сложно представить какие сложности могут возникнуть при написание компонентов на хуках вместо классов. Это можно будет узнать только в полевых условиях.
                                +4
                                Мало того что врут о сложности классов, так ещё и говорят что классы делают что-то ненадежным! При чем тут классы? Это facebook может делать горячую перезагрузку ненадежной, но не классы. Кроме того класс есть класс и работа с ним понятна и логична. А тут на тебе. Классы сложны — используйте хуки, но только всегда держите в своей памяти, что они должны выполняться только при тех условиях, которые очень схожи с условиями пришествия в этот мир сатаны.

                                  +1

                                  Классы сложны в том плане, что нужно отдельно изменять контекст методов, заранее определять состояние, если у нас компонент с единственным методом как в первом примере, то чтобы сделать рендер эффективным приходится использовать библиотеки подобные bind либо создавать компонент класса, где бы мы могли передать ссылку на функцию, никто не запрещает использовать классы, если нравятся. Хуки решают проблему wrapper-hell оберток и дают нативную возможность делить состояние между компонентами.

                                    –1
                                    Это не классы сложны, а фейсбук придумал абстракцию, где нет разделения на инициализацию и обновление. В результате чего передача компоненту стрелочной функции ломает эту чудесную абстракцию.
                                      +2

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

                                        –3
                                        Вам сюда: habr.com/post/285540
                                          0

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

                                            +1
                                            Нет, человек говорит о том, что реакт можно было бы сделать лучше и стрелочные функции не приводили бы к лишним ререндерам. Правда это был бы уже не реакт, а что-то другое.
                                              +1

                                              Вы бы хоть пояснили как именно стрелочные функции ломают React, как приводят в лишним рендерам, и почему именно стрелочные (а обычные не приводят?). Я примерно догадываюсь про что идёт речь (shallowComparison in PureComputed & event handlers?), но не уверен. Это ведь может быть что-угодно. Телепаты все в отпуске.

                                                –1

                                                Вы всё правильно догадываетесь. Об этом пишут в каждой статье про оптимизацию приложений на реакте. Странно, что мистер "понимаю о чём говорю" не в курсе этого.

                                        +1
                                        Во-во. Хочешь избавится от обертки-хелл и нативную поддержку разделенного состояния — откажись от классов! Помните, много лет назад facebook ликовал, что он наконец-то убедил Абрамова в пользе классов? А теперь получается, что один Абрамов переубедил весь facebook! Он покусал их и обратил в свое, особое видение программирования — абромирование.
                                          +2
                                          никто не запрещает использовать классы, если нравятся

                                          Тут ведь такой момент. Они открытым текстом в F.A.Q. пишут, что компоненты-через-хуки это их новая основная парадигма. Т.е. именно это становится true-react-way. Предлагают новые вещи писать в этом стиле.


                                          Так что по сути довольно логично ожидать того, что новые фичи и удобства будут в первую (и наверное часто единственную) очередь касаться именно хуко-компонент. Можно, конечно, быть ренегатом, но оправдано ли. Кстати, судя по документации, хуки не работают в class-components. Даже useContext.


                                          Хуки решают проблему wrapper-hell оберток и дают нативную возможность делить состояние между компонентами.

                                          Кстати отчасти хуки решают wrapper-hell, который существует исключительно ввиду того, что многие годы HoC пропагандируются как лекарство от всех болезней. Даже context завезли в таком виде, что у меня на проекте до 7 обёрток на 1 <div/> уходит. Это ведь не классовая модель виновата. Это react-way нынче такой. Либа просто не предоставляет низкоуровневых инструментов, чтобы ряд вещей, вроде того же контекста, можно было сделать без HoC. Вот попробуйте реализовать useContext-hook самостоятельно. Не получится.


                                          В какой-то момент команду React-а похоже задрали эти HoC и было решение сделать ход… конём.

                                            +1
                                            Больше похоже, что классы вообще развиваться не будут, разве что в каких-то исключительных случаях. Выглядит так, как будто единственная причина необъявления классов deprecated — неохота переписывать свои десятки тысяч компонентов. Вот избавятся от них в стиле «что работает — не трогаем, но если нужно что-то изменить, то переписываем на хуки», тогда объявят :)
                                              0

                                              Классы про которые здесь идет речь, используются реактом лишь в роли DSL — метаязыка для описания компонентов. На основе этого описания реакт создает нормальные объекты по всем канонам. Проблема в том, что такому языку (класс, методы, наследование) местами недостает выразительности. Функциональный подход в этом плане дает больше гибкости и декларативности. В пределе наверно получится lisp. ))

                                                0
                                                Нет там никакого ФП, никакого лиспа и никакой выразительности(в ФП). ФП — это бренд и не более того и ничего от ФП там не осталось. Там магия, магия на сейдэффектах и с сайдэффектами. Это целиком и полностью противоречит всем базовым концепциям ФП.

                                                Сам подход так же противоречит ФП, а суть его в следующем. Что такое класс? Это некие операции над контекстом, а структура контекста определена статистически.

                                                И тут на сцену выходит жс и возникает вопрос/проблема. Динамический язык позволяет генерировать структуру в момент использования. И вот он вопрос, зачем нам заранее определять структуру, когда намного её генерировать в процессе? Именно поэтому в жс никогда и не нужны были классы, был конструктор, который сам организует начальную структур объекта. А далее уже пользователь/метод сам дополнит эту структуру. По-сути класс это просто некий вид статического описания структуры, который мене выразителен чем динамический.

                                                В react-hooks дошли до того, что просто магией добавили контекст в метод. А то, что там нету this — это не попытка эмулировать ФП, а рядовая жс-банальность.

                                                function Constructor() {
                                                this.property;//это просто возможность вытащить проперть наружу
                                                //это не нужно для организации контекста для методов.
                                                privateProperty;
                                                privateMethod() {privateProperty;}
                                                this.publicMethod = privateMethod;
                                                }


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

                                                Что из этого имеет какое-то отношение к ФП — неясно. А вот как оно противоречит базовым концепциям ФП — видно сразу.

                                                  +1
                                                  Пфф… Вы так говорите, будто ФП и лисп в частности запрещают сайд-эффекты и магию. Особенно угарно упоминание лиспа, который позволяет творить супер-магию, которая бедному js и не снилась.

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

                                                  Подход хуков ближе к ФП, потому что в юзеркоде не требует классов, а требует только функции.
                                                    –1
                                                    Пфф… Вы так говорите, будто ФП и лисп в частности запрещают сайд-эффекты и магию.

                                                    Да.

                                                    Особенно угарно упоминание лиспа, который позволяет творить супер-магию

                                                    Поведайте мне об этом, я очень много подобных заявлений слышал, но ни одного примера(реального) не видел.

                                                    которая бедному js и не снилась.

                                                    Js'y много снилось, да и js'ом всё не ограничивается.

                                                    Система без сайд-эффектов непригодна к использованию.

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

                                                    Суть ФП в изоляции сайд-эффектов.

                                                    А потом начинаются манёвры, и вот один из них. Да, можно напихать(и пихается) в ФП классы, циклы, переменные, мутабельность и прочее, а после говорить «а лисп может». Только вот мочь он должен в рамках ФП-парадигмы, а не в рамках других. Использование другой парадигмы по умолчанию определяет за ФП несостоятельность. Да, тут можно поспорить на тему «на сколько», но факт остаётся фактом — само по себе ФП несостоятельно.

                                                    Ну и да, суть чего угодно в изоляции чего угодно.

                                                    Реакт прекрасно с этим справляется.

                                                    С чем? Где же эта изоляция, если само api по умолчанию не является ФП?

                                                    Я даже зашел сюда: en.wikipedia.org/wiki/Functional_programming и проверил свои познания ФП — действительно, всё в ЖС и в ректе противоречит всем базовым концепциям. Объекты на уровне языка, переменные, состояния, отсутствие чистоты, прозрачность ссылок, отсутствие хоть какой-то системы типов, циклы. Хотя в википедии описываются уже современные реалии, сейчас в ФП записывают(задним числом) что угодно. Там уже и «циклы не только через рекурсию» и тому подобное. И даже этим концепциям оно противоречит.

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

                                                    Какой такой функциональный стиль? Это про что? Про сеттеры, про jsx, про объекты? Это всё ортогонально ФП.

                                                    Подход хуков ближе к ФП, потому что в юзеркоде не требует классов, а требует только функции.

                                                    Я уже выше объяснял — почему это манипуляции. Это такие же классы. Вы это благополучно проигнорировали.

                                                    И да, что за глупости? С каких пор использование функций стало ФП? Там наверное си, фортран, паскаль, тысячи их — это всё ФП, да?
                                                      0
                                                      У меня создается ощущение, что вы ненавидите ФП. Если ФП само по себе не самостоятельно, то почему Haskell считается чисто функциональным языком, но на нем можно писать программы?

                                                      В лиспах магия — макросы. Позволяет расширять поведение языка на лету.

                                                      Я даже зашел сюда: en.wikipedia.org/wiki/Functional_programming и проверил свои познания ФП — действительно, всё в ЖС и в ректе противоречит всем базовым концепциям. Объекты на уровне языка, переменные, состояния, отсутствие чистоты, прозрачность ссылок, отсутствие хоть какой-то системы типов, циклы
                                                      Зачем мешать жс и реакт, мы ведь только о реакте говорим? Это явное передергивание.

                                                      С чем? Где же эта изоляция, если само api по умолчанию не является ФП?
                                                      Юзер компоненты можно писать трогая только определенную часть АПИ.
                                                        –1
                                                        Ну какой тут можно сделать вывод — полное игнорирование моих тезисов, моей аргументации. Просто попытка кидаться лозунгами вида «ты просто меня не любишь».

                                                        Если ФП само по себе не самостоятельно, то почему Haskell считается чисто функциональным языком,

                                                        Как быстро вы забыли про лисп и начала про хаскел. Кстати, вы даже базовой терминологии не знаете. «чистый» — это не значит, что там ТОЛЬКО ФП, а это значит вот это: en.wikipedia.org/wiki/Purely_functional_programming

                                                        И тут уже у вас возникают проблемы, т.к. до этого вы утверждали:

                                                        Система без сайд-эффектов непригодна к использованию.


                                                        А википедия нас сообщает:

                                                        Purely functional programming may also be defined by forbidding changing-state and mutable data.


                                                        К тому же:

                                                        data Foo = Foo {x :: Integer, str :: String}
                                                        deriving (Eq, Ord, Show)

                                                        Насколько я понимаю, википедия мне врёт? И это на самом деле не хаскель? А это переменные, структуры. Да ещё и операторы вида a op b.

                                                        но на нем можно писать программы?

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

                                                        такое, такое, такое, такое и такое.

                                                        А это я открыл только один файл, причём абсолютно рандомный.

                                                        В лиспах магия — макросы. Позволяет расширять поведение языка на лету.

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

                                                        Зачем мешать жс и реакт, мы ведь только о реакте говорим?

                                                        Наверное потому, что организация api в реакте использует свойства/парадигму жс?

                                                        Кстати, почему вы не продолжили говорить про реакт? Продолжайте.
                                                        Это явное передергивание.

                                                        Действительно, всё что вы обзовёте передёргиванием им становится.

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

                                                        Пошли манёвры, ну предоставьте пример использования api, который бы не противоречил базовым принципам ФП. Это ведь так просто.

                                                          +1
                                                          Кстати, вы даже базовой терминологии не знаете. «чистый» — это не значит, что там ТОЛЬКО ФП, а это значит вот это: en.wikipedia.org/wiki/Purely_functional_programming
                                                          Ну сходил бы и почитал твою же статью. «Чистое» ФП — это подмножество ФП. «Чистому» ФП противопоставляется «нечистое» ФП.

                                                          Как только всё это сталкивается с реальность — там начинаются манёвры, и вы их уже демонстрировали.
                                                          такое, такое, такое, такое и такое.
                                                          Вы просто пользуетесь тем, что словари далеки от идеала. Вы используете максимальное кастрированное определение ФП, которое банит даже хаскелль. Или тролль, или просто хейтер.

                                                          Я вас не об этом спрашивал, я спрашивал о конкретных, полезных и применимых примерах использования подобной магии
                                                          Ок, есть к примеру, clojure.core.async, который использует внутри себя макросы для превращения кода в стейт-машину.

                                                          Наверное потому, что организация api в реакте использует свойства/парадигму жс?
                                                          Но нет, api в реакте использует только подмножество JS, а не все в JS. Кроме того, никто не запрещает эмулировать ФП на не-ФП языках.

                                                          Пошли манёвры, ну предоставьте пример использования api, который бы не противоречил базовым принципам ФП. Это ведь так просто.
                                                          Держи: const Hi = () => <span>Hi</span>
                                        +1

                                        "Мы не делаем фреймворк, мы делаем либу для реактивной отрисовки компонентов" — говорили они...


                                        Блин, вместо того, чтобы как-то организовать merge этих всех wrapper'ов в виде официального api они начали изобретать mobx, но в своём собственном стиле. Зачем? Просто потому что правят балом именно они, и они это знают.


                                        Жаль, что они начинают вылезать из своих изначальных "обязанностей", задекларированных раньше. А когда люди начали вокруг этой либы строить нормальные решения, они начали думать: "почему-бы всё же не быть полностью фреймворком, ведь мы сможем сделать всё круче и быстрее остальных!"


                                        Ну что же, будем ждать, сколько ещё времени проживёт реакт в нормальном виде, до становления полной копии ng.

                                          –2
                                          *лучшей копии ng
                                          0

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

                                            0
                                            Ой, я наконец-то внимательно прочитал доку. Раньше я думал, что хуки нельзя использовать внутри компонентов которые внутри цикла. А вот оно как на самом деле:
                                            Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function
                                            Это решается линтером.
                                            +1
                                            Зато можно выкинуть recompose и других ребят и делать всё более-менее однотипно
                                            Имхо, ни победа, ни поражение, просто движение вбок (да, привет миксинам)
                                              0
                                              Вот, да, каждый новый проект как будто на чём-то совсем другом написан.
                                                0

                                                Часть кода можно переписать. Но вообще recompose решает немного другие проблемы и делает это классно. Как минимум тестить удобнее чистые функции, а не сайдэффекты.

                                                0
                                                До дружелюбия vuejs все равно очень далеко. Кстати, не понимаю людей, которые тянут эти реактовские штуки в него. Уже выпустили github.com/yyx990803/vue-hooks, примеры зачем-то пишут в рендер-функциях на jsx. Такое ощущение, что подсознательно не могут отказаться от сложного в пользу простого.
                                                  0
                                                  Где там jsx в примерах?
                                                    0
                                                    На конкретно в этом репозитории (vue-hooks) нет, хотя и там используется затрудняющае понимание рендер функция (зачем? чем плохо иметь template, подсвечиваемый в редакторе, с работающим intellisence, очень похожий на html (или pug)?).
                                                  0
                                                  Мне лично идея очень нравится, но вот возврат тупла как-то не очень. Кто-то видит плюсы по сравнению с возвратом объекта с read-only полем значения и методом его установки? а может просто с использованием геттеров.сеттеров нативных?
                                                    0
                                                    github.com/reactjs/rfcs/pull/68#issuecomment-433135283
                                                    В двух словах — проще именовать, ибо setState не знает, как правильно вернуть вашу проперти и какой у вас нэйминг конвеншн. Может у вас CamelCase)
                                                      0

                                                      А деструктуризации я не думал, разве что в крайнем случае. Я имел в виду не проще ли делать


                                                      const name = useState('Name');
                                                      return <input value={name.value} onChange={e => name.setValue(e.target.value)} onChange={e => name.value = e.value } />;
                                                        0

                                                        Это как скрестить ежа с ужом. value здесь играет роль prop, и для него хочется легкий произвольный нейминг. Объект бы добавил лишний уровень вложенности что усложнило сравнение и вездесущее но бессмысленное слово .value. setState() не меняет value. Поэтому результат это не объект, а две отдельные штуки. А две отдельные штуки это tuple (плохо, что нет в JS).

                                                      0

                                                      Видимо, что не пришлось пользоваться точечной объектной нотацией и не быть привязанным к конкретным полям вроде value | setValue. Пример:


                                                      const color = useState('red');
                                                      <ColorPicker color={color.value} onChange={color.set}/>
                                                      
                                                      const [color, onChange] = useState('red');
                                                      <ColorPicker {...{ color, onChange}}/>

                                                      Предложенный вами вариант сейчас используется в ref-ах, где нужно обращаться к полю current. В итоге в кодовой базе полно строк вида:


                                                      const block = this.blockRef.current;
                                                      // use it
                                                        0

                                                        Вот понятно что, чтобы её избежать, но не понятно зачем её избегать, особенно учитывая что this не использовать

                                                          0

                                                          Просто так удобнее, ИМХО. К примеру, я обычно пишу код в стиле:


                                                          render(){
                                                            const { var1, var2, var3 } = this.props;
                                                            const { var4, var5 } = this.state;
                                                            const { var6, var7 } = var3;
                                                          
                                                            return // use all of them
                                                          }

                                                          Так меньше всякой мишуры получается в наиболее перегруженной части (непосредственно JSX). Стараюсь поддерживать состояние JSX предельно простым.

                                                            0
                                                            Я обычно это использую, чтобы от this избавиться, то есть { props, state } = this
                                                              +1

                                                              Мне кажется, что для JSX кода ниже нет особой разницы откуда пришло значение из props или из state. Главное что оно есть. Поэтому логичнее не this убрать, а вообще всё лишнее, т.е. прицелиться уже к конкретным значениям. Скажем разве это принципиально в <ColorPicker color="state.color"/>, что color в state? Моя логика такая. К тому же часто в props-ах какие-нибудь составные объекты — их тоже удобнее разобрать на нужные запчасти до применения. В общем суть — убрать визуальный шум и оставить только саму суть.

                                                                +1
                                                                Ну в коде с которым я работаю нередко встречаются одинаковые названия свойств в пропсах и стейте
                                                      0
                                                      function useFriendStatus(friendID) {
                                                        const [isOnline, setIsOnline] = useState(null);
                                                        // ...
                                                      }
                                                      


                                                      Разве здесь не должен быть useState(ChaAPI.friendsStatus[friendID].isOnline), и что тогда будет, если придет значение с другим friendID? Поскольку это initialState, то значение isOnline не изменится. Как это разрулить хуками?
                                                        0
                                                        Подразумевается, что пока не пришло сообщение от API, на которое мы подписались, то статус нам неизвестен и API уведомит нас когда он станет известен. Может при подписке сразу дернёт колбэк, чтобы сообщить уже известный, а может отправит запрос и дернёт когда ответ придёт. Пока колбэк не дернули наш, у нас статус «лоадинг».

                                                        Хук жёстко привязан к стэку вызовов, включая параметры, как я понимаю. Если придёт новый friendID, то создастся новый стейт и новая подписка
                                                        +3
                                                        Выглядит как наичернейшая магия. КАК это всё работает? Где хранится? Как трекается?

                                                        И получается, каждый вызов функции – читай «каждый рендер» – приводит к вызовам всех этих хуков? На каждый рендер мы будем порождать целые охапки новых функций (хотя одного раза более чем достаточно)?

                                                        Я не владею в должной мере этой информацией – но даже если это никак не влияет на производительность (расскажите, кто разбирается) – неужели вот чисто концептуально это хорошо и правильно? Никогда этого не понимал. Я мирюсь с инлайновыми функциями для function-as-children и для map – но вот это:

                                                              <button
                                                                onClick={() => dispatch({type: 'reset', payload: initialCount})}>
                                                                Reset
                                                              </button>
                                                              <button onClick={() => dispatch({type: 'increment'})}>+</button>
                                                              <button onClick={() => dispatch({type: 'decrement'})}>-</button>
                                                        


                                                        это же попросту ужасно выглядит, это очень плохо читается, и это НЕ_НУЖНО™. Очевидно же, что эти коллбеки делают одно и то же. На кой ляд их пересоздавать?

                                                        Зато, безусловно, любая обезьянка, едва усвоившая синтаксис стрелочных функций, но ниасилившая этот чудовищно сложный this и жуткие классы, сможет написать рабочий компонент, это да… А потом Фейсбук сочинит какой-нибудь очередной Fiber 19.0, который в очередной раз позволит всему этому хозяйству не лагать.
                                                          0
                                                          В документации есть описание как это работает под капотом
                                                            +1
                                                            В каком плане они делают одно и то же? У нас не гарантировано (если не лезть в код или документацию useReduce), что при каждом рендеринге (каждом вызове функции компонента) будет одно и то же значение у dispatch. Можно вынести эти стрелочные функции за «скобки» jsx, но всё равно должно быть замыкание на dispatch, получаемый при рендеринге. Ну может какая-то оптимизация типа мемоизации функций от dispatch
                                                              0

                                                              Для этого они сделали useEventCallback().

                                                              0
                                                              Там используется та же техника, что я описывал тут: habr.com/post/413791
                                                              Если вкратце, то у компонента есть массив в который он по мере обращения к хукам пушит состояние. При перезапуске рендера он это состояние уже не пушит, а берёт из этого массива и смещает указатель вперёд. Именно с этим и связан запрет использования ветвлений циклов и тп. Ведь тогда состояние будет скакать между хуками приводя к непонятным глюкам.
                                                                –2
                                                                Я не владею в должной мере этой информацией – но даже если это никак не влияет на производительность (расскажите, кто разбирается) – неужели вот чисто концептуально это хорошо и правильно? Никогда этого не понимал. Я мирюсь с инлайновыми функциями для function-as-children и для map – но вот это:

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

                                                                это же попросту ужасно выглядит, это очень плохо читается, и это НЕ_НУЖНО™. Очевидно же, что эти коллбеки делают одно и то же. На кой ляд их пересоздавать?

                                                                Правильно, только это проблемы не колбеков, не стрелочных функций, а redux. Это абстракция вида логика в данные, данные в логику. Это проблема этой абстракции. Как вы иначе преобразуете onClick в необходимый набор данных для вызова dispatch? А никак.

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

                                                                Зато, безусловно, любая обезьянка, едва усвоившая синтаксис стрелочных функций, но ниасилившая этот чудовищно сложный this и жуткие классы, сможет написать рабочий компонент, это да… А потом Фейсбук сочинит какой-нибудь очередной Fiber 19.0, который в очередной раз позволит всему этому хозяйству не лагать.

                                                                Сам реакт является жертвой этого подхода, а fiber не лечит стрелочные функции, а лечит реакт. Который и является(по-сути) тем самым redux внутри, со всеми вытекающими тормозами.

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

                                                              Самое читаемое