Counter

Counter demo built with Expressive MVC - the complete source below runs as an editable sandbox when JavaScript is enabled.

App.tsx

import './App.css';

import Button from './common/Button';
import { Component } from '@expressive/react';

// Yes, it's a class with render(). No, it's nothing like the class
// components you were told to avoid: no setState, no lifecycle methods,
// no useState/useCallback/useMemo, no dependency arrays.
//
// Fields are reactive - assign one and the view updates. Methods are
// auto-bound, so you can pull them off `this` and they still work.
// One class is the state AND the view.
class Counter extends Component {
  current = 1;

  // Declared methods, not arrows - binding is intrinsic, not lexical.
  // That's why destructuring them in render() below is safe.
  increment() {
    this.current++;
  }

  decrement() {
    this.current--;
  }

  reset() {
    this.current = 1;
  }

  render() {
    const { current, increment, decrement, reset } = this;

    return (
      <div className="container">
        <h1>Counter Example</h1>
        <div className="counter">
          <Button onClick={decrement}>{'−'}</Button>
          {/* Click the number to reset. */}
          <pre onClick={reset}>{current}</pre>
          <Button onClick={increment}>{'+'}</Button>
        </div>
      </div>
    );
  }
}

// It's the element and the state in one - no hook, no provider.
export default () => <Counter />;

App.css

/* Counter is a minimal hero - center the title with its controls. */
.container {
  text-align: center;
}

.counter {
  display: flex;
  align-items: center;
  justify-content: center;
}

.counter pre {
  font-size: var(--t-2xl);
  font-weight: 500;
  width: 5rem;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  text-align: center;
  cursor: pointer;
  color: var(--fg);
}

.counter .button {
  width: 56px;
  height: 56px;
  font-size: var(--t-xl);
}