2

I would like to have the initial state for a date TextField be set to undefined until the user has selected a date, and then allow the user to easily reset the date back to undefined.

In the following code, the Reset button will properly reset parameters.count state to 0, but it will not reset parameters.date to undefined. Is undefined treated specially in this case?

import React, { useState } from "react";
import { Button, TextField } from "@material-ui/core";

import "./App.css";

export type Parameters = {
  count: number;
  date?: string;
};

const initialParameters: Parameters = {
  count: 0,
  date: undefined,
};

export const App: React.FC = () => {
  const [parameters, setParameters] = useState<Parameters>(initialParameters);

  const handleCountChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setParameters({
      ...parameters,
      count: Number(event.target.value),
    });
  };

  const handleDateChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setParameters({
      ...parameters,
      date: event.target.value,
    });
  };

  const resetState = () => {
    setParameters(initialParameters);
  };

  return (
    <div className="App">
      <TextField
        type="number"
        label="Count"
        value={parameters.count}
        onChange={handleCountChange}
      />
      <TextField
        label="Date"
        type="date"
        value={parameters.date}
        onChange={handleDateChange}
        InputLabelProps={{
          shrink: true,
        }}
      />
      <Button onClick={resetState}>Reset</Button>
    </div>
  );
};
phoenix
  • 7,988
  • 6
  • 39
  • 45
  • 1
    I think it's more a case of the TextField component not accepting undefined as a value (should be an empty string instead). It's in fact surprising that it doesn't show the warning that the input is changing from controlled to uncontrolled, but maybe Material UI doesn't show those warnings. – Guy Incognito Oct 07 '20 at 17:04
  • 3
    In fact, ``'s [spec suggests](https://www.w3.org/TR/html52/sec-forms.html#ref-for-valid-date-string%E2%91%A8) that if the value is an invalid date string, the value is set to an empty string: "_If the value of the element is not a valid date string, then set it to the empty string instead._" Try setting the initial value of `date` to `''` instead of `undefined`. – cbr Oct 07 '20 at 17:05
  • Setting `date: "",` did work, thank you. I guess I was trying to use the type system to encode "missing" value, but it looks like the `TextField` wants `""`. – phoenix Oct 07 '20 at 17:08

1 Answers1

2

The solution based on @cbr's comment is to coerce undefined to "" before passing to TextField.

import React, { useState } from "react";
import { Button, TextField } from "@material-ui/core";

import "./App.css";

export type Parameters = {
  count: number;
  date?: string;
};

const initialParameters: Parameters = {
  count: 0,
  date: undefined,
};

export const App: React.FC = () => {
  const [parameters, setParameters] = useState<Parameters>(initialParameters);

  const handleCountChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setParameters({
      ...parameters,
      count: Number(event.target.value),
    });
  };

  const handleDateChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setParameters({
      ...parameters,
      date: event.target.value,
    });
  };

  const resetState = () => {
    setParameters(initialParameters);
  };

  // TextField expects blank string to represent no value.
  const dateDisplay = parameters.date ? parameters.date : "";
  return (
    <div className="App">
      <TextField
        type="number"
        label="Count"
        value={parameters.count}
        onChange={handleCountChange}
      />
      <TextField
        label="Date"
        type="date"
        value={dateDisplay}
        onChange={handleDateChange}
        InputLabelProps={{
          shrink: true,
        }}
      />
      <Button onClick={resetState}>Reset</Button>
    </div>
  );
};
phoenix
  • 7,988
  • 6
  • 39
  • 45