Pull to refresh
10
0
Send message

Это почему же? Размер кода увеличивается в сравнении с обычным на одну строчку - пишем useInjection перед вызовом хука. На перфоманс влияния вообще нет т.к. контекс никогда за время работы приложения не меняется, следовательно никакого перерендеринга не будет

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

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

Лично я с такой проблемой не сталкивался, но даже если страница загружается долго, уверены ли вы что это именно веб браузер долго не может отобразить вашу страницу? Или это ответ с сервера долго не приходит? Или еще в чем проблема? И да, повторюсь, если вы достоверно знаете, что, скажем, из-за размера бандла страдает user experience, то да, эта проблема, которую нужно решать, если нет - есть другие важные вещи, которые необходимо учитывать

Преждевременная оптимизация - известный антипаттерн, а Дональд Кнут вообще назвал ее корнем всех зол. Если размер бандла, скорость рендеринга и потребление памяти удовлетворительные, т.е. не вызывает проблем у пользователя, то эти цифры не имеют значения. А я вас уверяю, в приведенном примере todo листа эти все показатели будут удовлетворительными. Чистота и количество кода здесь более интересный показатель, другое дело что опять таки, показанный пример совершенно недостаточен для того, чтобы сделать вывод, todo лист без проблем можно реализовать даже на jQuery, что автор и показал, но для более сложных случаев, jQuery превращает код в императивный ад.

Rimworld - отличная современная игра а-ля Dwarf Fortress

Хуки тоже дают инкапсуляцию на самом деле. То, что они принимают и возвращают - это публичный интерфейс из данных и методов для работы с ними, а что они скрывают внутри - вызывающий код и знать не знает. Другое дело что да, удобство хуков по сравнению с классами работает хорошо до тех пор, пока этих самых вызовов хуков немного. И пока в них направо и налево не начинают передаваться коллбеки. Поэтому я, например, стараюсь избегать использование всяких там useEffect/useCallback и прочих в компоненте напрямую, оборачивая их во что-то более удобоваримое (кастомные хуки/HOC)

Тоже делал что-то подобное только несколько иначе. Что мне не нравится в предложенной реализации (уже обо многом сказали выше):

  • Отправкой формы и то, как это реализуется (урл и т.д.) - это не задача этого хука, нарушается SRP.

  • Форма собирается из массива данных - тут две проблемы: во-первых мешаются в кучу данные и представление. Я предпочитаю, когда представление реализуется в jsx/html-темплейте, а не в Json-е. Иначе такой код очень трудно воспринимать. Во-вторых, подумайте что вы будете делать, если некоторые ваши инпуты перестанут быть обычными инпутами, а станут селектами, мультиселектами, текстареями, автокомплитами и т.д. Во что превратиться ваша конфигурация такой формы и как дорого вам обойдется добавление нового типа поля? А если бы вы просто собирали вашу форму в jsx и подвязывали все нужные пропсы ручками - реализовали бы все что угодно без лишней головной боли

  • Валидация может быть какая угодно, не только required/minLength и т.д. Если мы настраиваем модель валидации, и получаем объект с ошибками валидации, то мы должны учесть любые кастомные правила и то, что у одного поля может быть несколько ошибок валидации, которые неплохо бы отобразить одновременно

Я тоже делал что-то подобное на TypeScript. Там я описывал саму модель, схему валидации, например:

interface SoilLayerVM {
    soil_id: string;
    depth: string;
}

const validationScheme: ValidationScheme<SoilLayerVM> = {
    soil_id: {
        required: Validators.required
    },
    depth: {
        required: Validators.required,
        number: Validators.float,
        range: Validators.range(0, 1000)
    }
};

И тоже делал похожий хук:

const { errors, setFormField, validate } = useForm(model);

Вызывал validate когда нужно (здесь onValidate - это кастомный проп, не то же самое что одноименный HTML-атрибут, ну да не важно, можно в onSubmit если хочется)

<Form onValidate={() => validate(validationScheme)} ...>

а errors, setFormField подвязывал к компонентами вручную, типа

<FormTextField label="Глубина, м" value={model.depth} onChange={e => setFormField('depth', e.target.value)} 
    error={!!errors?.depth} errorHintComponent={
        <>
            {errors?.depth?.required && <FormHintText>Поле является обязательным</FormHintText>}
            {errors?.depth?.number && <FormHintText>Введенное значение не является числом</FormHintText>}
            {errors?.depth?.range && <FormHintText>Значение должно быть в диапазоне [0-1000]</FormHintText>}
        </>
    } />

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

Да нет тут никакой магии, это обычный React Context

Ну, давайте так покажу, если foo и bar сбивают с толка.

Было

const someResult = useMyHook();

Стало

const { useMyHook } = useInjection();
const someResult = useMyHook();

А я кажется возможно не туда смотрю. Вы реализовали ДИ по средством самого контекста? то есть контекст === ДИ.

Контекст === DI контейнер. Снаружи мы кладем зависимости в контекст, изнутри - извлекаем зависимости из контекста

Конечно же мне не надо будет идти в useInjection и что-либо там менять. Что ж, попробую на пальцах.

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

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

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

  • В тесте мы положим в наш DI-контейнер не настоящую функцию-хук, а Mock. Для этого мы просто обернем тестируемый компонент в Context Provider, с помощью которого заменим содержимое DI-контейнера. Теперь при выполнении компонент все также извлечет функцию из контейнера по тому же самому ключу, но на месте настоящего хука окажется его фейковая реализация. Никаких изменений в коде компонента или функции useInjection нам для этого делать не потребуется.

На самом деле, думаю что любая библиотека, реализующая DI (упомянутая в обсуждении inversify-react или type-di, да все что угодно) делает ровным счетом то же самое: кладет зависимости в контейнер, а компонент (или что там их будет использовать) - извлекает их из DI-контейнера по определенному ключу. Регистрируя разные реализации зависимостей по одним и тем же ключам можно внедрять ту или иную реализацию, не изменяя код того, кто их использует.

Это аналогично позднему связыванию при вызове методов объекта, когда адрес вызова вычисляется не в момент компиляции, а в момент вызова. Только в случае виртуальных методов неизвестный на момент компиляции адрес вычисляется по известному индексу виртуального метода в некой таблице, здесь неизвестная на момент компиляции ссылка на зависимость вычисляется по известному на момент внедрения зависимости ключу в DI-контейнере.

где у вас все это ?

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

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

Мде...

Я так понимаю вопрос в принципе о том, для чего нужен DI. Как бы тут коротко ответить... Если говорить умными словами, то DI реализует принцип инверсии управления, что уменьшает связанность между модулями/компонентами/сервисами приложения, т.к. они перестают зависеть друг от друга, вместо этого оба зависят от некого абстрактного контейнера и интерфейсов/типов, а не от реализации. Это позволяет нам собирать композицию из таких модулей как нам захочется, а не так как жестко забито в коде. То, что во втором случае компонент зависит от useInjection - не проблема, наша задача отделить модуль верхнего уровня (компонент, отвечающий за UI-презентацию) от модуля нижнего уровня (хук, отвечающий за некую бизнес-логику или получение данных). Т.е. во втором случае мы функцию нашего хука получим используя useInjection как зависимость.

Когда мы тестируем компонент (если конечно речь не об интеграционных тестах), мы хотим именно протестировать сам UI компонент и то, что он корректно отображает данные, которые ему предоставлены, нам не нужно чтобы тест отвалился, скажем, из-за нестабильного интернет-соединения, в случае если наш хук делает http-запрос. Да и даже если наш хук работает криво - тест нашего компонента не должен из-за этого отвалиться - для этого мы должны написать тест нашего хука и пусть отвалится именно он. В тестах мы не будем делать http-запрос, мы заменим наш модуль нижнего уровня Mock-ом, который вернет тестовые данные как если бы они были получены по HTTP

Как частно вы встречались с необходимостью подменять зависимость во время работы вашего приложения? Как правило DI контейнер, будучи однажды сконфигурирован, таким и останется навсегда. Это не динамические данные, которые меняются многократно в процессе выполнения вашего приложения. Т.е. я исхожу из того, что ререндеринга не будет происходить никогда. Если же у вас такой хитрый DI, что зависимости подменяются на лету ежесекундно, то Вам безусловно такое решение не подойдет.

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

Рад, что удалось привлечь Ваше внимание своей публикацией. Насчет надежнее - использование сторонней библиотеки, какой бы хорошей она ни была, надежнее чем React Context? Чем именно?

Возможно Вы путаете с React.SFC или я ошибаюсь?

Хоть PubSub можно и самому написать за 5 минут без лишних выкрутасов:

Разумеется, это достаточно простой в реализации паттерн. В принципе, в моем варианте, класс, реализующий собственно PubSub, занимает 44 строчки кода вместе с импортами, отступами и JSDoc-комментариями. Все остальное - это выкрутасы для декораторов. Спасибо за комментарий!

PS: RxJS на минималках - это скорее другая моя поделка: https://github.com/YMSpektor/x-vars

На собеседовании искренность — ваш конек!

А можно на вопрос "кем вы видите себя через 5 лет" искренне ответить, что рассчитывал на искреннюю беседу, а не на шаблонные вопросы с шаблонными же ответами?

1

Information

Rating
Does not participate
Registered
Activity