简体   繁体   中英

How to update React state with a data object using hooks

I have a component with a date picker and a would like to change the value of the date picker in the state, using hooks, and also the date in the date picker itself, so you can select a date, or you can click a button to set a date, like 30 days ago.

this is what I tried so far:

const MyComp = () => {

  const [formData, setFormData] = useState({
    dateFrom: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
    dateTo: new Date()
  });

  const handleDateClick = interval => {
    setFormData({...formData, dateFrom: new Date(Date.now() - interval * 24 * 60 * 60 * 1000)});
    setFormData({...formData, dateTo: new Date()});
  };

  const handleOnChange = e => {
    setFormData({...formData, [e.target.name]: e.target.value});
  };

  return (
    <Button onClick={() => handleDateClick(30)} variant="secondary">30 days ago</Button>

    <DatePicker
      selected={formData.dateFrom}
      name="dateFrom"
      dateFormat="MMMM d, yyyy"
      onChange={value => handleOnChange({target: {name: "dateFrom", value}})}
    />
);

}

clicking the button has no effect, not even displaying any error, the state is not updated and the value in the datepicker does not change? Any idea please?

The reason why you're seeing a strange behavior is because your handleDateClick is calling setFormData twice in a row, which is a problem because setFormData it is asynchronous, meaning that it will not immediately reflect and update your state.

See this SO question for more details:

So, essentially what ends up happening is that you update dateFrom but then call setFormData again right after, and since it uses old state , you're never seeing an updated value for dateFrom .

const handleDateClick = interval => {
  setFormData({ ...formData, dateFrom: ... })

  // uses the value of `dateFrom` from the "old state"
  // when you do `...formData`, then updates `dateTo`
  setFormData({ ...formData, dateTo: ... })
}

I suggest you combine all your updates inside handleDateClick into a single setFormData call. From what it looks like, there's no reason for you to have two separate calls.

Here's how you should approach this instead:

function Button({ children, ...rest }) {
  return <button {...rest}>{children}</button>;
}

function MyComp() {
  const [formData, setFormData] = React.useState({
    dateFrom: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
    dateTo: new Date()
  });

  const handleDateClick = interval => {
    setFormData({
      dateTo: new Date(),
      dateFrom: new Date(Date.now() - interval * 24 * 60 * 60 * 1000)
    });
  };

  return (
    <>
      <Button onClick={() => handleDateClick(30)}>Click me!</Button>
      <p>
        <b>State:</b> {JSON.stringify(formData)}
      </p>
    </>
  );
}

Here's a working example if you'd like to see:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM