DEV Community

Pavel Kříž
Pavel Kříž

Posted on

MithrilJS component with state management

This is my 11th write up on dev.to site. MithrilJS component and state management. A substantial part of the code was written by AI. Source code included.

MithrilJS

Mithril is an excellent JS framework, tiny and powerful. Like with another JS frameworks you can write components - a small piece of code solving a dedicated subtask.

State management

Most of applications and most of JS frameworks implement state management - the way state is realized and maintained.

There are many ways to implement state management in Mithril. Excellent source of information is a blog post of Kevin Fiol: Simple State Management in Mithril.js

Component

In this example I have SVG component, logical OR gate. It has two inputs (A, B) and one output (Q).
You can intend either internal component state (so component is independent) or global app state. And this is what I show you in this example.

State

const NodeState = (size) => Array(size).fill(false);

const NodeActions = (state) => ({
  setNode: (index, value) => {
    if (index >= 0 && index < state.length) {
      state[index] = value;
      m.redraw();  // Ensure Mithril redraws the view to reflect state changes
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

OR gate

const ORGate = {
  view: ({ attrs: { state, actions, idxA, idxB, idxQ } }) => {
    const inputA = state[idxA];
    const inputB = state[idxB];
    const output = inputA || inputB;

    // Update the output node state
    actions.setNode(idxQ, output);

    return m('div',
      m('svg', {
          viewBox: '0 0 100 100',
          width: '100',
          height: '100',
          xmlns: 'http://www.w3.org/2000/svg'
        },
        [
          // Input lines
          m('line', { onclick: () => actions.setNode(idxA, !inputA), x1: '0', y1: '30', x2: '30', y2: '30', stroke: inputA ? 'red' : 'blue', 'stroke-width': '2' }),
          m('line', { onclick: () => actions.setNode(idxB, !inputB), x1: '0', y1: '70', x2: '30', y2: '70', stroke: inputB ? 'red' : 'blue', 'stroke-width': '2' }),

          // OR gate rectangle
          m('rect', { x: '30', y: '20', width: '40', height: '60', fill: 'none', stroke: 'black', 'stroke-width': '2' }),

          // Output line
          m('line', { x1: '70', y1: '50', x2: '100', y2: '50', stroke: output ? 'red' : 'blue', 'stroke-width': '2' }),

          // Labels for inputs and output
          m('text', { onclick: () => actions.setNode(idxA, !inputA), x: '13', y: '25', 'text-anchor': 'middle', 'font-size': '12' }, 'A = ', inputA ? '1' : '0'),
          m('text', { onclick: () => actions.setNode(idxB, !inputB), x: '13', y: '65', 'text-anchor': 'middle', 'font-size': '12' }, 'B = ', inputB ? '1' : '0'),
          m('text', { x: '86', y: '40', 'text-anchor': 'middle', 'font-size': '12' }, 'Q = ', output ? '1' : '0'),
          // sign in gate
          m('text', {x: '45', y: '40', 'text-anchor': 'right', 'font-size': '18'}, "≥1")
        ]
      ),
    );
  }
};
Enter fullscreen mode Exit fullscreen mode

Short explanation

There is an array of values (initially: false values) called nodes. Nodes are used as fictive nodes in real logical circuit. Every gate has two inputs ("connected" to nodes) and one output ("connected" to node). All nodes are in one array; index is used to work with specific node (item of array). Indexes of nodes for inputs and output are put to the component via attributes.

m(ORGate, { state: nodeState, actions: nodeActions, idxA: idxA, idxB: idxB, idxQ: 2 }),
Enter fullscreen mode Exit fullscreen mode

Source code

Complete source code with visualization is on flems playground.
Hope this is useful for you, developers. A little note: chatGPT AI ​​did most of the work.

Top comments (0)