В библиотеке mobx
есть очень интересный примитив атом, который создается через createAtom
. С его помощью можно создавать очень интересные конструкции и вообще построить чуть ли не всю архитектуру приложения. Что нам говорит документация:
`createAtom`
{🚀} Usage: `createAtom(name, onBecomeObserved?, onBecomeUnobserved?)`
Creates your own observable data structure and hooks it up to MobX. Used internally by all observable data types. Atom exposes two `report` methods to notify MobX with when:
- `reportObserved()`: the atom has become observed, and should be considered part of the dependency tree of the current derivation.
- `reportChanged()`: the atom has changed, and all derivations depending on it should be invalidated.
А давайте немного перезавернем его, чтобы в дальнейшем удобнее было им пользоваться.
import { IAtom, createAtom } from 'mobx';
export function createAtomSubscriber(name: string, subscribe: () => () => void): IAtom {
let cancel: undefined | (() => void);
return createAtom(name, () => {
cancel?.();
cancel = subscribe();
},
() => {
cancel?.();
cancel = undefined;
});
}
Теперь мы можем заворачивать в createAtomSubscriber
любые подписки на любые источники данных. Например, у представим компонент, специально немного усложненный, для большей наглядности.
const Component: FC = () => {
const [[prevData, currentData], setData] = useState(['', '']);
const onMessage = useCallback((e) => setData([currentData, e.data]), [currentData]);
useEffect(() => {
window.addEventListener('message', onMessage);
return () => window.removeEventListener('message', onMessage);
}, [onMessage])
return <>{/**/}</>
}
Здесь мы храним предыдущее и текущее состояние, которое приходит к нам из postmessage. Поскольку мы используем предыдущей состояние функция onMessage
постоянно пересоздается и тем самым тригеррит переподписку в useEffect
. И самое плохое в этой ситуации, что данный код будет работать, но крайне неоптимально. Конечно, этот компонент можно переписать и оптимизировать только средствами react. Но как мы помним, этот пример достаточно синтетический и в реальности можно попасть в такую ситуацию, в менее очевидных условиях.
Давайте теперь перепишем компонент, чтобы вообще избегать подобных ситуаций и воспользуемся createAtomSubscriber
class Model {
constructor() {
makeObservable(this, {
data: observable.ref,
onMessage: action,
});
}
data: [string, string] = ['', ''];
atom = createAtomSubscriber('message', () => {
window.addEventListener('message', this.onMessage);
return () => window.removeEventListener('message', this.onMessage);
});
onMessage = (e) => {
this.data = [this.data[1], e.data];
}
}
const Component = observer<{model: Model}>(({ model }) => {
model.atom.reportObserved();
return <>{/**/}</>;
});
Подписка произойдет, когда атом начнет наблюдаться с помощью observer
или на первого рендера, а отписка когда компонент отмонтируется. Тут главное не забыть вызвать atom.reportObserved()
а все остальное за нас сделает mobx
.