Comment by suis_siva
15 days ago
In my experience, the biggest problem in Elm is how unintuitive TEA's docs are. In some ways, Elm really feels like Haskell -- you want to avoid creating "stateful" modules and you prefer stateless modules.
For example, https://harmont.dev's app (landing page is not Elm) has a component to render a pipeline graph called DagGraph. In React, you'd do something like
function DagGraph() {
const steps = useState<Jobs[]>([]);
const selectedJobIdx = useState<number | null>(null);
return ...;
}
In elm, I lift this up (I will keep using React syntax for the sake of wider-audience readability)
function DagGraph({ steps, selectedJobIdx } : IDagGraphModel) {
return ...;
}
This allows for better testability of the graph view. The parent then injects the model
function PipelinePage({ graphMode, selectedPipeline }: IPipelineModel) {
return (
graphMode === GraphMode.DagView
? <DagGraph steps={selectedPipeline.steps} selectedJobIdx={selectedPipeline.selectedStep} />
: <TableView steps={selectedPipeline.steps} selectedJobIdx={selectedPipeline.selectedStep} />
);
}
Now -- we need to bubble up the selected pipeline index based on when the user clicks a node in the DAG view. I personally prefer to do this completely orthogonally to the view rendering pipeline (which remains mostly pure). I create a new model, and define the message relationships to it:
namespace PipelineState {
interface Model {
steps: Step[];
selectedStep: number | null;
};
interface ISelectStepMessage { stepIdx: number };
interface IUnselectStepMessage;
type Message = ISelectStepMessage | IUnselectStepMessage;
function update(m: Model, msg: Message): Model { return...; }
}
// main model
interface Model {
pipeline: PipelineState.Model,
}
type Message = PipelineState.Message | SomeOtherMessage | ...;
function update(m: Model, msg: Message): Model {
switch (msg) {
case PipelineState.Message as msg:
return update(m, msg);
...
}
}
And this seems to work quite well. Personally, I really like this architecture, because it enables me to think about state and UI separately. One of the really big gripes I have with React is how most components end up with `useEffect` and `useState`, and then immediately become untestable. Elm literally doesn't allow for that.
At the end of the day, there's a trade-off -- my logic is no longer localized to the "component" relevant to rendering the pipeline DAG. I never have one file open editing Elm code, but I'm a vim user so it doesn't bother me. In some ways, semantically, I think it makes sense to split the "state backend" of your UI from your UI, in my opinion.
Most of my app states are represented as state machines (coming from embedded this is quite natural) and UI is represented as pure transformations of that state.
With Elm, it really feels like you reason about state and UI separately, and I actually prefer that, both for testability and that's just how my brain works.
No comments yet
Contribute on Hacker News ↗