Behold the HTML State
Tags: redux, state, html, a11y
Published: 2019-05-21 01:37:32

---
title: Behold the HTML State
published: true
description: Redux? Component State? Hooks? You ain't gonna need it.
tags: redux, state, html, a11y
cover_image: https://thepracticaldev.s3.amazonaws.com/i/2c7gqkh2odshekpfaiu3.jpeg
---
Let's pick a simple example - a few __tabs__ with a simple __checkboxes__ inside. But, regardless all the simplicity let's make it a bit more complicated that it should, and then optimize the approach making it better and better every step.

Let's try `Redux`, `setState`, `useState` and... the end would be... unexpected.

> TL;DR https://codesandbox.io/s/romantic-http-h6cgj

## The App

Let's assume that our `App` would be something like
```js
function App() {
  return (
    <div className="App">
      <TabHeader>
        <Tab id={1}>One</Tab>
        <Tab id={2}>Two</Tab>
        <Tab id={3}>Three</Tab>
      </TabHeader>
      <TabContext>
        <Tab id={1}>
          Content of tab1 <CheckBox />
        </Tab>
        <Tab id={2}>
          Content of tab2 <CheckBox />
        </Tab>
        <Tab id={3}>
          Content of tab3 <CheckBox />
        </Tab>
      </TabContext>
    </div>
  );
}
```

I wish to have the first example as _worse_ as possible, but I think this would be enough...


## Redux
O Redux, the State of the State! You rule the single source of truth, dispatching the goods for all of us.

Of course using Redux to handle Tab component is a right decision, as long as Redux is always a right decision.

```js
// TabHeader just renders everything inside
const TabHeader = ({ children }) => <section>{children}</section>;

// "Stateless/Dumb" Tab component
const TabImplementation = ({ children, onSelect }) => <div onClick={onSelect}>{children}</div>;

// All the logic is in TabContext
const TabContextImplementation = ({ children, selectedTab }) => (
 <section>
 {React.Children.map(children, (child) => (
   // displaying only "selected" child
   selectedTab === child.props.id  
     ? child
     : null
  ))}
  </section>
)

// connecting Tab, providing a `onSelect` action
const Tab = connect(null, (dispatch, ownProps) => ({
  onSelect: () => dispatch(selectTab(ownProps.id))
}))(TabImplementation);

// connecting TabContext, reading the `selectedTab` from the state
const TabContext = connect(state => ({
  selectedTab: state.selectedTab
}))(TabContextImplementation);
```

That's actually quite simple example how `Redux` could wire your components, and how cool it is.

But, you know, probably we don't need such complex code for such simple example. Let's optimize!

## Component State

To use _own component state_ we just need to remove bindings to redux, and rewire `App`, making it a Stateful component.

```js
class App extends React.Component {
  state = {
    selectedTab: 1
  };

  onSelect = (selectedTab) => this.setState({selectedTab});

  render () {
  return (
    <div className="App">
      <TabHeader>
        <Tab id={1} onSelect={() => this.onSelect(1)}>One</Tab>
        <Tab id={2} onSelect={() => this.onSelect(1)}>Two</Tab>
        <Tab id={3} onSelect={() => this.onSelect(1)}>Three</Tab>
      </TabHeader>
      <TabContent selectedTab={this.state.selectedTab}>
        <Tab id={1}>
          Content of tab1 <CheckBox />
        </Tab>
        <Tab id={2}>
          Content of tab2 <CheckBox />
        </Tab>
        <Tab id={3}>
          Content of tab3 <CheckBox />
        </Tab>
      </TabContent>
    </div>
  );
 }
}
```

That's all the state management we need. Could it be simpler?

## Hooks

Would `hooks` make it even better? Yes! They would! Even if difference is minimal it's huge from readability point of view.
```js
const App = () => {
  const [selectedTab, setSelected] = useState(1);
  return (
    <div className="App">
      <TabHeader>
        <Tab id={1} onSelect={useCallback(() => setSelected(1))}>One</Tab>
        <Tab id={2} onSelect={useCallback(() => setSelected(1))}>Two</Tab>
        <Tab id={3} onSelect={useCallback(() => setSelected(1))}>Three</Tab>
      </TabHeader>
      <TabContent selectedTab={selectedTab}>
        <Tab id={1}>
          Content of tab1 <CheckBox />
        </Tab>
        <Tab id={2}>
          Content of tab2 <CheckBox />
        </Tab>
        <Tab id={3}>
          Content of tab3 <CheckBox />
        </Tab>
      </TabContent>
    </div>
  );
}
```

## We did it?
The difference between the first and the last example is ... different. Amount of code needed is almost the same, benefits are incomparable, readability is perfect in any case.

The only big difference is in "component-ization" - with local state and hooks the tab `State` is __local__ while with Redux it is global. It's nor bad, nor good, and both would serve you needs.

### Did we forget something?
We did a great _react_ job, but a poor _html_ job. Attaching `onClick` handlers to divs is a worst idea ever - it's absolutely not accessible.

> Is there a way to handle a "state", and make application "accessible" in the same time?

## HTML State

Let me first show you the code, then I'll explain how it works

```js
const TabHeader = ({ children, group }) => (
  <>
    {React.Children.map(children, (child, index) => (
      // we will store "state" in a radio-button
      <input
        class="hidden-input tab-control-input"
        defaultChecked={index === 0}
        name={group}
        type="radio"
        id={`control-${child.props.controls}`}
      />
    ))}
    <nav>{children}</nav>
  </>
);

const Tab = ({ children, controls }) => (
  // Tabs are controlled not via `div`, `button` or `a`
  // Tabs are controlled via `LABEL` attached to input, and to the tab itself
  <label htmlFor={`control-${controls}`} aria-controls={controls}>
    {children}
  </label>
);

const TabContent = ({ children, group }) => (
  <section className="tabs">
    {React.Children.map(children, (child, index) => (
      <section class="tab-section" id={child.props.id}>{child.props.children}</section>
    ))}
  </section>
);

const CheckBox = () => <input type="checkbox" />;

const App = () => (
  <div className="App">
    <TabHeader group="tabs">
      <Tab controls="tab1">One</Tab>
      <Tab controls="tab2">Two</Tab>
      <Tab controls="tab3">Three</Tab>
    </TabHeader>
    <TabContent>
      <Tab id="tab1">
        Content of tab1 <CheckBox />
      </Tab>
      <Tab id="tab2">
        Content of tab2 <CheckBox />
      </Tab>
      <Tab id="tab3">
        Content of tab3 <CheckBox />
      </Tab>
    </TabContent>
  </div>
);
```

That's all, there is NO STATE MANAGEMENT at all, and it works much better than before. Only `redio-buttons` with `labels` and `tabs`, not quite related to them... I am not sure how it works - but it works.

> Here is the proof - link to the sandbox - https://codesandbox.io/s/romantic-http-h6cgj

To be more concrete - it works as well as [reach-ui tabs](https://ui.reach.tech/tabs/) - try it, it's the same. It's the same _true accessible_ experience Reach is so passionate about.
But `reach-ui` has to __handle keyboard__ events and __focus management__ to make _tabs_ work as they should - only the `active` tab-header is focusable, and you might change tabs by pressing `Left`/`Right`. 

You may not belive - it my example it would work exactly the same.

## The secret

The secret is in CSS - every `input` is _visually hidden_. So it exists, it's focusable, but invisible. And everything else _uses_ `input` __state__.

```css
// input 1 is checked? Then tab 1 should be visible.
.tab-control-input:checked:nth-of-type(1) ~ .tabs .tab-section:nth-of-type(1){
  display: block;
}

// and label should be bold if corresponding radio button is active
.tab-control-input:checked:nth-of-type(1) ~ nav label:nth-of-type(1){
  font-weight: 600;
}

// and focus ring should be teleported to a label
.tab-control-input:focus:nth-of-type(1) ~ nav label:nth-of-type(1) {
  outline: thin dotted;
  outline: 5px auto -webkit-focus-ring-color;
}
```

There is a limitation with structure - the `~` operator needs `input` to be on one side, and `label` + `tab` on another. That means that all inputs would be on the left and all tabs would be on the right. As a result something like this

```js
.tab-control-input:checked ~ .tabs .tab-section {
  display: block;
}
```

would display all `.tabs` for any `input` checked, as long as they all match the condition. 

In this example I've used `:nth-of-type(N)`, but it's better to generate CSS on the fly (CSS-in-JS), with more _unique_ selectors, like `#category-women-clothing:checked ~ div nav label[for="category-women-clothing"]` (code snippet from [theurge](https://theurge.com)), or `input-0-0-1` like in Reach.

## Conclusion

So, SURPRISE! HTML could be your State Management, and you will get more _semantic_ and MUCH more accessible result out of the box. [Play with it](https://codesandbox.io/s/romantic-http-h6cgj) for a while :)

You know - use the platform :) You might miss something.