microsoft / fast

The adaptive interface system for modern web experiences.
https://www.fast.design
Other
9.27k stars 595 forks source link

fast-text-field not updated after the bound value changed #4106

Closed bsrdjan closed 3 years ago

bsrdjan commented 3 years ago

Describe the bug; what happened?

Programatic changes of the fast-text-field bound value not anymore reflected in fast-text-field ui field, after fast-text-field value once changed manually.

What are the steps to reproduce the issue?

  1. Create React app following these steps: https://www.fast.design/docs/integrations/react
  2. Add the change event listener to fast-text-field, to set the state value
  3. Run the app
  4. Press "Swap" button to change state value programatically, all works fine
  5. Change the input value manually, the state is also changed
  6. After manual change done once, programatic state changes are not any more reflected in input field value. Press "Swap" button, the fast-text-field remains unchanged.

What behavior did you expect?

Programatic state changes should be reflected after manual change.

If applicable, provide screenshots:

In what environment did you see the issue?

Darwin, Chrome 86.0.4240.183 (Official Build) (x86_64)

App.js

import "./App.css";

import {
  FASTDesignSystemProvider,
  FASTCard,
  FASTButton,
  FASTTextField,
  FASTTextArea,
} from "@microsoft/fast-components";

import React from "react";

//import h from "./react-wrappers";

FASTDesignSystemProvider;
FASTCard;
FASTButton;
FASTTextField;
FASTTextArea;

class App extends React.Component {
  constructor(props) {
    super(props);
    this.title = "fast-react";
    this.state = {
      firstname: "Max",
      lastname: "Mustermann",
    };
    this.firstRef = React.createRef();
    this.lastRef = React.createRef();
    this.elLastInput = null;
  }

  componentDidMount() {
    this.elLastInput = this.lastRef.current;
    this.elLastInput.addEventListener("change", this.onChange);
  }

  componentWillUnmount() {
    this.elLastInput.removeEventListener("change", this.onChange);
  }

  onClick = (evt) => {
    this.setState({
      firstname: this.state.lastname,
      lastname: this.state.firstname,
    });
    console.log("click", this.state);
  };

  onChange = (evt) => {
    const { id, value } = evt.target;
    this.setState({ [id]: value });
    console.log("change", this.state);
  };

  render() {
    return (
      <fast-design-system-provider use-defaults>
        <fast-card>
          <h2>{this.title}</h2>
          <h4>
            {this.state.firstname} {this.state.lastname}
          </h4>
          <input
            id="firstname"
            ref={this.firstRef}
            value={this.state.firstname}
            onChange={this.onChange}
          ></input>
          <fast-text-field
            id="lastname"
            ref={this.lastRef}
            value={this.state.lastname}
          ></fast-text-field>
          <fast-button onClick={this.onClick}>Swap</fast-button>
        </fast-card>
      </fast-design-system-provider>
    );
  }
}

export default App;
EisenbergEffect commented 3 years ago

@bsrdjan I believe we have fixed this in master and it should be in the next release. @nicholasrice Can you confirm?

nicholasrice commented 3 years ago

Your implementation is binding to the value HTML attribute, not the value DOM property, which represents the stateful value. React does not currently expose a declarative way to bind to the property (as opposed to the attribute) like other libraries do; see https://www.fast.design/docs/integrations/react#html-attributes and https://www.fast.design/docs/integrations/react#interop-layers-skatejsval-and-reactify-wc for details on the issue and approaches to circumvent.

bsrdjan commented 3 years ago

Thanks @nicholasrice