ref() Multiple

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

App.tsx

import './App.css';

import State, { ref } from '@expressive/react';

const FIELDS = [
  { key: 'first', label: 'First name' },
  { key: 'last', label: 'Last name' },
  { key: 'email', label: 'Email' }
] as const;

// `ref(this)` is the plural form: one call hands back an imperative handle
// for *every* field, keyed by name. Each `refs.first` is a callable ref -
// `refs.first('Ada')` writes the field - so one proxy drives the whole form
// and a loop can clear every field, with no useRef per input.
class Profile extends State {
  first = '';
  last = '';
  email = '';

  refs = ref(this);

  clear() {
    for (const { key } of FIELDS)
      this.refs[key]('');
  }
}

function Form() {
  const profile = Profile.use();
  const { refs } = profile;
  const filled = FIELDS.filter(({ key }) => profile[key]).length;

  return (
    <div className="container form">
      <h1>Ref Proxy</h1>
      <p>
        <code>ref(this)</code> exposes one callable ref per field. Every input
        writes through that proxy, and Clear loops it over all three.
      </p>

      <div className="fields">
        {FIELDS.map(({ key, label }) => (
          <label key={key}>
            {label}
            <input
              value={profile[key]}
              onChange={(e) => refs[key](e.target.value)}
            />
          </label>
        ))}
      </div>

      <footer>
        <small>{filled} of {FIELDS.length} filled</small>
        <button onClick={() => profile.clear()}>Clear all</button>
      </footer>
    </div>
  );
}

export default () => <Form />;

App.css

.form {
  max-width: 26rem;
}

.fields {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  width: 100%;
}

.fields label {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  text-align: left;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--muted);
}

.form footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  width: 100%;
}