Comments 36
Это огромный шаг в функциональную сторону, только смущает, что в React объекте теперь хранится текущий обрабатываемый узел дерева (в общем просто глобальная переменная). На порядок круче было бы принимать какой-то io объект как props:
const MyComponent = ({ props }, io) => {
  const [count, setCount] = io.useState('count');
};
Тогда уже
const MyComponent = ({ props }, io) => {
  const [count, setCount] = useState(io, 'count');
};

чтоб свои хуки можно было делать единообразно.
Ну вот они подумали-подумали, и решили io убрать :)
Так содержимое компонента лезет во внешний scope и нельзя мокать useState без костылей.

Какую же функциональную, если эти хуки состоят из сайд-эффектов чуть менее, чем полностью?

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

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


К примеру, функция put 5 в Хаскеле возвращает значение типа State Int (), и сколько бы раз ее ни вызывали — это будут совершенно эквивалентные State Int (). Если написать вот так — let a = put 5; b = put 5 in ..., то никаким тестовым сценарием вы не обнаружите разницы в поведении a и b.


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

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

ПС А имперавтивный код — ну да, одна большая монада.

Вы путаете результат вызова функции и значение которое будет передано второму операнду операции >>=.


Так, get в монаде State всегда возвращает функцию \s -> (s,s), независимо от того сколько раз и когда вызывается.


А вот в выражении get >>= (\x -> ...) параметр x может оказаться связанным с любым значением. Но это никак не нарушает чистоту функции.

Мы говорим о разных вещах. "=" в javascript не то же что "=" в haskell. Иногда это "<-". И тогда результат вызова функции зависит от состояния в монаде. Фактически, монада — один из аргументов функции в такой записи и этот аргумент добавляется неявно, и поэтому некорректно говорить о том, что два последовательных вызова функции с одними и теми же аргументами вернут одно и то же значение. Иначе например чтение чисел с клавиатуры было бы невозможно.
если эти хуки состоят из сайд-эффектов чуть менее, чем полностью

Это уже просто смешно. Ладно раньше недалекие верующие не видели процедурности dispatch'a — там необходимо немного подумать, так что это для верующих простительно. Но ведь тут написан очевидный процедурный код, да даже с процедурными названиями!

const setOn = () => setLight(0)


Но все-равно божья роса, кричат, что это ФП.

Кстати, а как же оптимизация? Мы ведь каждый раз создаем функции setOn и setOff каждый раз и передает в компоненты ниже как props. В итоге, компонент тоже обязан перерисоваться, хотя, де факто, пропсы не менялись.

А там для этого есть дичьuseCallback и React.memo. После просмотра примеров придётся промыть глаза с мылом :)

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

class MyItem {
    @observable counter = 1;

    @action increase = () => this.counter++;
}

@observer
class MyItemRenderer extends React.Component {
  render () {
    const { item } = this.props;

    return <div onClick={item.increase}> {item.counter} </div>;
  }
}

В обсуждаемом примере count/counter — это состояние, а не свойство, так что лучше сравнивать с вот этим кодом:


@observer
class MyItemRenderer extends React.Component {
  @observable counter = 1;
  @action.bound increase() { this.counter++; }

  render () {
    return <div onClick={this.increase}> {this.counter} </div>;
  }
}
Почему лучше? useState — это квинтэссенция отвратительного дизайна. Какой смысл стараться повторить то, что они написали, если нету никакого смысла писать так, кроме желания повыё, как круто и функционально (на самом деле нет) вы пишете. Отказ от классов ради отказа от классов.
Вот есть код из статьи (дурацкая привычка вставлять код картинками):
Скрытый текст
image


А вот как это пишется на MobX:
class Counter {
  @observable value = 1;
  
  constructor (initialValue) {
    this.value = value;
  }

  @action increment = () => this.value++;
  @action decrement = () => this.value--;
}

@observer
class App extends React.Component {
  render () {
    const { counter } = this.props;

    return (
      <div>
        <p>{counter.value}</p>
        <button onClick={counter.increment}>Increment</button>
        <button onClick={counter.decrement}>Decrement</button>
      </div>
    );
  }
}


И не надо говорить, что стейт должен создаваться во время рендера — это дурацкая идея и ничего общего с «задачей» тут нету.
Можете нажать на картинки где много кода, там ссылка на CodeSandBox, наверное Вы правы, нужно вставлять код текстом

На той картинке ничего компоненту App не передается. Откуда в вашем коде взялся counter в props?


Зачем менять внешний контракт компонента делая разные версии не взаимозаменяемыми?


И не надо говорить, что стейт должен создаваться во время рендера — это дурацкая идея и ничего общего с «задачей» тут нету.

Вот этот как раз детали реализации. Где вы в моем коде увидели создание стейта при рендере?

Где вы в моем коде увидели создание стейта при рендере?

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

Зачем менять внешний контракт компонента делая разные версии не взаимозаменяемыми?

Потому что версия, которая предлагается в статье — не должна существовать.

На той картинке ничего компоненту App не передается. Откуда в вашем коде взялся counter в props?

А у меня — передается. В этом ведь (частично) суть.

Суть — в локальном состоянии для компонента. В старом API React это делалось через this.state/this.setState, в новом — через useState. В mobx-react локальное состояние компонентов делается через свойства с декоратором @observable. Это три механизма — прямые аналоги друг друга, и остаются таковыми даже когда один из них не имеет права существовать.


Или вы в принципе отрицаете возможность компонентов иметь свое состояние?

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

Не обязательно, но предпочтительно. В примере, очевидно, у нас логика и состояние вью-модели, логику презентации конечно нужно не выносить :)
Или вы в принципе отрицаете возможность компонентов иметь свое состояние?

Все, я понял теперь вашу аргументацию.

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

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

Ну так как бы и @observer для классов был реализован не в mobx, а в mobx-react! А про сохранение API я ничего и не говорил.

Окей. Но в чем суть аргумента? Да, MobX собираются поддерживать эту фичу. Это никак не говорит о качестве этой фичи. И никак не говорит об отношении авторов MobX к этой фиче. Даже не говорит, на базе какого кода получается лучше код. На самом деле это ни о чем не говорит кроме того, что MobX, как фреймворк, который, по сути, строится вокруг реакта поддерживает его новые возможности.

У меня уже давно закралось подозрение, что все эти pureComputed, React.memo и прочие штуки для производительности — вообще не являются "линией партии" и созданы просто, чтобы не ныли "а чо там медленно всё?". Во всех мануалах, гайдах, оф.документации, да почти везде мы видим примеры кода, в которых при обновлении node на самом верху, мы перестраиваем весь vDomTree целиком вниз, а потом реконсилируем его. И типа нормально, нечего переживать, ведь это же vDom, он же быстрый. о_О. Вот даже в примерах про Hook-и — всё тоже самое. Не удивительно, что они от классов отказываются.

Что касается redux'а вообще и dispatch'а в частности, так это то, что евангелисты несли маркетинговую чушь про особый подход (не то, что этот убогий MVC), функциональщину, хотя сам redux — это обычный конечный автомат, state machine, в котором совершенно ООПшным процедурным образом дёргается store.dispatch({}) с передачей обыкновенного сообщения, как это было у Алана Кея и как это давным давно реализовано в WinAPI, только там обработчик — это WindowProc, а здесь — reducer.


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

useState  —  позволяет нам писать чистые функции с доступом к state в них.
useContext  -  позволяет писать в них чистые функции с контекстом.
useRef  —  позволяет писать чистые функции, которые возвращают изменяемый объект ref.

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

С того момента как представили React Hooks, сообщество было в восторге от этой фичи

Вот тут вижу частичное укрывание правды. Если сообщество так хорошо их восприняло, то почему тогда Дэн Абрамов начал просветительскую деятельность по поводу хуков в твитере?


Почему вообще не освящён негативный аспект хуков? Почему всё подаётся под соусом "это ещё одна мега-фича которую вы должны начать юзать" и не указываются границы применимости (единственное, что указали — это про производительность в хуках useEffect)

Only those users with full accounts are able to leave comments. Log in, please.