Слабая связность и высокая переиспользуемость представлений + логики
- Высокая переиспользуемость БЛ
Несколько безсвязных фич, могут использовать внутри себя другую фичу со всеми ее вытекающими. Композиция фич на уровнеeffector
- Абстракция БЛ
Не нужно знать насколько сложная в нижней фиче логика, если работает один раз, будет работать всегда, главное передавать нужные данные - Дробление на уровне
effector
Таким образом можно разбивать большие фичи на маленькие переиспользуемые, и лучше разделять зоны отвественности
-
Легко выстрелить себе в ногу устаревшим значением
Если форвардить значение с нескольких сторов в другой стор, и при этом каким-то образом один из сторов получаетundefined
в хэндлере, то стор в который форвардят будет хранить старое значение -
Необходимость в активации роута
При переходе на роут, сторы верхнего уровня (хост) должны будут вызвать апдейт, чтобы данные дошли нижнего уровня -
Неочевидные связи
Когда несколько фич связываются таким образом, сложность связей возрастает
В этом примере есть 3 фичи, dollar
, euro
, calculator
. Калькулятор является универсальным, т.е. его можно использовать для подсчетов как долларов, так и евро.
// calculator/model.js
import { createStore, combine } from "effector";
export const $exRate = createStore(0);
export const $input = createStore(0);
export const $kzt = combine($input, $exRate, (input, exRate) => {
return parseInt(input, 10) * exRate;
});
export const $calcUI = combine({
exRate: $exRate,
input: $input,
kzt: $kzt
});
Калькулятор зависит только от своих сторов, все что она делает это рендерит свои сторы и не знает ничего о том что снаружи
import React from "react";
import { useStore } from "effector-react";
import { $calcUI } from "./model";
const Calculator = () => {
const { exRate, kzt } = useStore($calcUI);
return (
<div>
<div> Exchange rate: {exRate} </div>
<div> KZT: {kzt} </div>
</div>
);
};
export default Calculator;
Теперь мы хотим переиспользовать эту логику в контексте долларов и евро, для этого все что нужно сделать, это передать (forward
) нужные значения в калькулятор
// euro/model.js
import { createStore, createEvent } from "effector";
import { $exRate, $input } from "../calculator/model";
import { routeForward } from "../../utils";
export const $euroInput = createStore("0");
const $euroRate = createStore(436);
export const evChangeInput = createEvent();
$euroInput.on(evChangeInput, (_, val) => val);
routeForward({
from: $euroInput,
to: $input,
route: "euro"
});
routeForward({
from: $euroRate,
to: $exRate,
route: "euro"
});
Мы заставили калькулятор считать евро!
import React from "react";
import { useStore } from "effector-react";
import Calculator from "../calculator/Calculator";
import { evGoTo } from "../../router";
import { $euroInput, evChangeInput } from "./model";
const Euro = () => {
const input = useStore($euroInput);
return (
<div>
<h1> Euro to KZT </h1>
<input onChange={ev => evChangeInput(ev.target.value)} value={input} />
<Calculator />
<button onClick={() => evGoTo("dollar")}> Dollar </button>
</div>
);
};
export default Euro;
Примерно тоже самое у нас и для долларов
Обратите внимание на routeForward
, это forward
с активацией на определенном роуте, то есть при переходе на /dollar
передаются актульные значения со сторов:
export const routeForward = ({ from, to, route }) => {
const routeFrom = sample(from, evGoTo.filter({ fn: path => path === route }));
// forward from value when you go to path
forward({
from: routeFrom,
to
});
// forward from when from itself is updated
forward({
from: from,
to
});
};
Первый forward
передаст значение сторов при переходе на определенный роут, это необходимо чтобы переиспользуемая фича имела актуальные данные, в роуте которого мы находимся
Второй forward
, обычный, передает значение сторов при их обновлении
Таким образом калькулятор и его логику можно переиспользовать везде, а о деталях ее имплементации можно забыть. Эта статья не является гайдом для прямого использования, а скорее она для предоставления ментальной модели переиспользуемых фич, у вас детали реализации такого паттерна могут отличаться