Skip to content

Make input-wrapping components controllable by React #439

@pavestru

Description

@pavestru

Are you reporting a bug or a feature?

  • Bug (or is it a feature?)

Expected Behavior

The recommended way to implement forms with React is to use controlled components. That means that the state of React component controls the state of forms input elements, like checked, value or disabled. Changing React state from outside changes state of form input elements.

Actual Behavior

Currently custom elements like axa-checkbox wrap form elements and create a facade for their attributes. In case of axa-checkbox, it wraps an <input type="checkbox" /> with attribute checked. However, these attributes do not reflect well between the custom element and its own wrapped inputs. For example, in case of axa-checkbox, when set checked={true} from outside, it gets reflected to input's checked="checked" correctly once, but changing axa-checkbox's checked attribute further from outside does not change the checked attribute of the input element.

However, interacting with the element itself directly (e.g. by clicking) changes its internal state and is able to trigger event listeners.

Observed in components:

  • axa-choice
  • axa-checkbox
  • (possibly others)

Steps to Reproduce the Problem

Notice when clicking first time on AxaCheckbox, its input's internal checked state is changed to unchecked first (independently of the app state) and from the 2nd click on is in sync with state:

controlled_axa-checkbox

// App.js
import React, { Component } from "react";
import "@axa-ch/patterns-library/dist/components/a-checkbox/index.js";

class App extends Component {
  state = {
    checked: false
  };
  componentDidMount() {
    this.checkbox.addEventListener("change", this.onCheckBoxChange, {
      passive: false
    });
  }
  onCheckBoxChange = e => {
    const checked = e.target.checked;
    this.setState({ checked });
  };
  render() {
    return (
      <div>
        State: {JSON.stringify(this.state.checked)}
        <axa-checkbox
          checked={this.state.checked}
          name="test-name2"
          ref={el => (this.checkbox = el)}
        >
          AxaCheckbox
        </axa-checkbox>
        <label>
          <input
            type="checkbox"
            checked={this.state.checked}
            onChange={this.onCheckBoxChange}
          />
          Native checkbox
        </label>
      </div>
    );
  }
}

export default App;

Specifications

  • Browser: Chrome 66 on macOS

Metadata

Metadata

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions