Как стать автором
Обновить

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

1) Берешь MobX
2) Наслаждаешься

А если еще и state-tree прикрутить то вообще сказка
https://github.com/mobxjs/mobx-state-tree


В итоге код вообще становиться похожим на Vuex только в React
Единственное что может Vuex но не может нормально mobx-state-tree это нормальный вызов одних action из других. Но эта фича скорее костыль...

А вот это уже не нужно, это как раз избыточная дополнительная кодовая нагрузка.

Некоторые так говорят вообще про все js фремворки разом =)


Как по моему. Если это упрощает работу, улучшает стабильность и при этом кушает не сильно много — то почему бы и нет.

Упрощает работу? — Не согласен, только усложняет нагромождениями кода.
Улучшает стабильность? — Если использовать TS, то это не актуально. Если использовать JS, то может быть незначительно, но это не стоит того, чтобы писать такой код.
На 100%

Хоть и перевод, но стоит отметить, что в хуке не происходит отписка от Observable. Да и в самом Observable такого функционала нет.

Вроде все на месте.
subscribe возвращает функцию


 return () => {
            this._listeners = this._listeners.filter(l => l !== listener);
        };

которая возвращается из useEffect


return observable.subscribe(setVal);

Лол, только позавчера выбросил vuex в одном из проектов. Просто надоело то, как плохо оно все ложится на ts. Даже думал статеечку на хабр запилить, но там получилось все проще, чем можно ожидать. Упрощенно:
class ServiceFactory
{
get MyService(): MyService
{
//в оригинале инстанс здесь кешируется
retutn Vue.observable(new MyService());
}
}
А инстанс ServiceFactory уже можно запихнуть как плагин vue, да и вообще куда угодно.
О vuex пока добрым словом не вспоминал, подводных камней пока не нашел

Типичная статья про «убийцу» Redux:

Впервые прочитав официальный туториал по Redux, меня больше всего поразил большой объем кода, который мне пришлось написать, чтобы изменить состояние. Изменение состояния требует объявления нового action, реализации соответствующего reducer и, наконец, отправки action

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

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

Щито? Добавить 3 строчки для production и development, чтобы подключить Redux extension — это слишком высокая цена?
Дальше предлагается краткое описание замены и дальше идёт тот же самый «большой объём кода», только с видом сбоку.

Большой объем кода только из за демонстрации подхода, если обернуть все это в библиотеку получится вполне себе компактное решение.


Щито? Добавить 3 строчки для production и development, чтобы подключить Redux extension — это слишком высокая цена?

Тут речь не про цену подключения, а про использования Redux ради возможности использовать extension.

Где именно там компактное решение? Весь подход предлагаемого решения только в том, чтобы избавиться от 3 строчек кода с описанием action.

    toggleTodo(index: number) {
        this.todos.set(this.todos.get().map(
            (todo, i) => (i === index ? { text: todo.text, completed: !todo.completed } : todo)
        ));
    }


Господи, мои глаза :( И автор оригинальной статьи учит нас чему-то про Redux и удобно ли с ним работать. У меня больше нет вопросов и замечаний.
Где именно там компактное решение?

С возможным решением можно ознакомится в статье https://habr.com/ru/post/483526/
Будет выглядеть так:


toggleTodo(index: number) {
  this.draft.todos[index].completed = !this.draft.todos[index].completed       
}

Всегда пугали подобные конструкции, когда ради изменения одного флажка у ОБЪЕКТА мне зачем то нужно ре-сет сделать для все коллекции. Поэтому я пишу на Vue.

Я по возможности вместо такой записи:
const todos = useObservable(todoService.todos);
const filter = useObservable(todoService.visibilityFilter);
const visibleTodos = getVisibleTodos(todos, filter);

стремился бы к такой:
const storeState = useObservable(todoService);
const visibleTodos = todoService.getVisibleTodos(filter);

Этого достаточно просто достичь объявлением одного Observable


readonly state = new Observable({
  todos: [] as Todo[], 
  filter: VisibilityFilter.SHOW_ALL
});

const storeState = useObservable(todoService.state);
const visibleTodos = todoService.getVisibleTodos(storeState.filter);
Да, но тогда реализации из статьи теряется возможность подписываться на изменения только filter или только todos. Допиливать понадобиться.

Это пара строчек кода


function useStore<TState, TResult>(
  store: SimpleImmutableStore<TState>,
  project: (store: TState) => TResult,
): TResult {

export function useObservable<T, R>(observable: Observable<T>,  
+selector: (value: T) => R): R {
-    const [val, setVal] = useState(observable.get());
+    const [val, setVal] = useState(selector(observable.get()));

    useEffect(() => {
-        return observable.subscribe(setVal);
+        return observable.subscribe(value => setVal(selector(value)));
    }, [observable]);

    return val;
}

Вот так писать нельзя, этот код содержит ошибку:


export function useObservable<T>(observable: Observable<T>): T {
    const [val, setVal] = useState(observable.get());

    useEffect(() => {
        return observable.subscribe(setVal);
    }, [observable]);

    return val;
}

Между рендером и его фиксацией значение observable может измениться, и вы этого никогда не узнаете.


Правильный вариант — вот такой:


export function useObservable<T>(observable: Observable<T>): T {
    const [val, setVal] = useState(observable.get());

    useEffect(() => {
        setVal(observable.get());
        return observable.subscribe(setVal);
    }, [observable]);

    return val;
}

А может быть, даже вот такой (зависит от того, что позволено делать в методе subscribe):


export function useObservable<T>(observable: Observable<T>): T {
    const [val, setVal] = useState(observable.get());

    useEffect(() => {
        const s = observable.subscribe(setVal);
        setVal(observable.get());
        return s;
    }, [observable]);

    return val;
}

Но этот код на самом деле не является типобезопасным. Если вдруг ваш тип T является функцией, то подобная реализация будет работать совершенно не так как задумывалось! Правильнее будет написать как-то так:


export function useObservable<T>(observable: Observable<T>): T {
    const [val, setVal] = useReducer((state, action) => action, observable.get());

    useEffect(() => {
        const s = observable.subscribe(setVal);
        setVal(observable.get());
        return s;
    }, [observable]);

    return val;
}

Но и этот код всё ещё не идеален, при смене самого observable будет лишний рендер. И как избавиться от него по-нормальному — я придумать не смог.

Те же проблемы. inputFactory нельзя изменить, State не может быть функцией… Разве что проблемы с методом .get() нету, за отсутствием такого метода.

Что бы что то изменилось в observable перед отработкой useEffect должно произошло другое асинхронное событие. Вы можете продемонстрировать такое поведение?

function Foo({ observable: Observable<string>() }) {
    useEffect(() => observable.next("foo"));
    return <div></div>
}

Хоть код и авторский, но добавил вашу правку, спасибо.

Vue and Vuex не нравится что во vue они расширяют глобальный скоуп. В больших проектах это аукнится. К тому же разговор про то что Vue использует Alibaba сомнительный, не видел и строчки кода на Vue там.

По поводу react reduxэто классика. сейчас effector топ…
Vue and Vuex не нравится что во vue они расширяют глобальный скоуп
Можно подробнее про это? А то я не в курсе. И там нет простого способа избежать этого?
Похоже вы просто не пробовали MobX
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории