Objects are not valid as a React child using DatePicker

57 Views Asked by At

I'm trying to pass a Date object as payload to my formSlice but I keep getting the Error that Objects are not valid as React child or Invalid time Range when I pass the date as string with ToString() or ToLocaleString(). I have read that it's better to have only primitive types for state in Redux.

Here is how my formSlice looks like in Redux store:

import { createSlice } from "@reduxjs/toolkit";

const formSlice = createSlice({
  name: 'form',
  initialState: {
    firstName : '',
    lastName : '',
    dateOfBirth : '',
    userName : '',
    password :  ''
  },
  reducers: {
    changeFirstName(state, action){
      state.firstName = action.payload;
    },
    changeLastName(state, action){
      state.lastName = action.payload;
    },
    changeDateOfBirth(state, action){
      state.dateOfBirth = action.payload
    },
    changeUsername(state, action){
      state.userName = action.payload
    },
    changePassword(state, action){
      state.password = action.payload
    }
  }
});

export const {
  changeFirstName,
  changeLastName,
  changeDateOfBirth,
  changeUsername,
  changePassword
} = formSlice.actions;

export const formReducer = formSlice.reducer;

This is my form component:

import { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import ReactDOM  from "react-dom";
import DatePicker from "react-datepicker";
import {
  addCustomer,
  changeFirstName,
  changeLastName,
  changeDateOfBirth,
  changeUsername,
  changePassword
} from '../store/index.js';
import 'react-datepicker/dist/react-datepicker.css';

function Form ({ onClose, children, actionBar }) {
  useEffect(() => {
    document.body.classList.add('overflow-hidden');

    return () => {
      document.body.classList.remove('overflow-hidden')
    };
  },[]);

  const form = useSelector((state) => {
    return state.form;
  });

  const dispatch = useDispatch();

  const handleFirstNameChange = (event) => {
    dispatch(changeFirstName(event.target.value));
  }

  const handleLastNameChange = (event) => {
    dispatch(changeLastName(event.target.value));
  }

  const handleUserNameChange = (event) => {
    dispatch(changeUsername(event.target.value));
  }

  const handlePasswordChange = (event) => {
    dispatch(changePassword(event.target.value));
  }

  const handleDateOfBirthChange = (date) => {
    //let formattedDate = formatDate(date);
    //console.log(formattedDate);
    dispatch(changeDateOfBirth(date));
  }

  const handleSubmit = (event) => {
    event.preventDefault();
    dispatch(addCustomer(form))
    onClose();
  }

  return ReactDOM.createPortal(
    <div className="fixed inset-0 flex items-center justify-center" onClick={onClose}>
      <form
        onClick={(e) => e.stopPropagation()}
        className="bg-white shadow-md rounded-md p-8 w-full max-w-md"
        onSubmit={handleSubmit}
      >
        <h2 className="text-2xl font-bold mb-4">Add Customer</h2>
        <div className="mb-4">
          <label
            htmlFor="firstName"
            className="block text-sm font-medium leading-6 text-gray-900 mb-1"
          >
            First Name
          </label>
          <input
            type="text"
            value={form.firstName}
            onChange={handleFirstNameChange}
            name="firstName"
            id="firstName"
            className="mb-1 block w-full border-2 border-blue-300 bg-blue-100 rounded-md py-1.5        pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
          />
        </div>
        <div className="mb-4">
          <label
            htmlFor="lastName"
            className="block text-sm font-medium leading-6 text-gray-900 mb-1"
          >
            Last Name
          </label>
          <input
            type="text"
            value={form.lastName}
            onChange={handleLastNameChange}
            name="lastName"
            id="lastName"
            className="block w-full border-2 border-blue-300 bg-blue-100 rounded-md py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6 mb-1"
          />
        </div>
        <div className="mb-4">
          <label
            htmlFor="dateOfBirth"
            className="block text-sm font-medium leading-6 text-gray-900 mb-1"
          >
            Date of Birth
          </label>
          <DatePicker
            placeholderText="YYYY/MM/DD"
            selected={form.dateOfBirth}
            onChange={handleDateOfBirthChange}
            dateFormat="yyyy-MM-dd"
            className="block w-full border-2 border-blue-500 bg-blue-100 rounded-md py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6"
          />
        </div>
        <div className="mb-4">
          <label
            htmlFor="userName"
            className="block text-sm font-medium leading-6 text-gray-900 mb-1"
          >
            User Name
          </label>
          <input
            type="text"
            value={form.userName}
            onChange={handleUserNameChange}
            name="userName"
            id="userName"
            className="block w-full border-2 border-blue-300 bg-blue-100 rounded-md py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6 mb-1"
          />
        </div>
        <div className="mb-4">
          <label
            htmlFor="password"
            className="block text-sm font-medium leading-6 text-gray-900 mb-1"
          >
            Password
          </label>
          <input
            type="text"
            value={form.password}
            onChange={handlePasswordChange}
            name="password"
            id="password"
            className="block w-full border-2 border-blue-300 bg-blue-100 rounded-md py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6 mb-1"
          />
        </div>
        <div className="mt-4">
          <button
            type="submit"
            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
          >
            Submit
          </button>
        </div>
      </form>
    </div>,
    document.querySelector('.modal-container')
  );
} 

I have tried to convert date to string in selected prop but still getting an error.

<DatePicker
  placeholderText="YYYY/MM/DD"
  selected={form.dateOfBirth.toString()}
  onChange={handleDateOfBirthChange}
  dateFormat="yyyy-MM-dd"
/>
1

There are 1 best solutions below

0
Drew Reese On

You can serialize, e.g. stringify, the dateOfBirth value when dispatching to the store. You'll then need to de-serialize dateOfBirth when reading it back out of the store to be passed to the DatePicker component.

Example:

...

const handleDateOfBirthChange = (date) => {
  dispatch(changeDateOfBirth(date.toString()));
}

...

<DatePicker
  placeholderText="YYYY/MM/DD"
  selected={form.dateOfBirth ? new Date(form.dateOfBirth) : new Date()}
  onChange={handleDateOfBirthChange}
  dateFormat="yyyy-MM-dd"
  className="block w-full ..."
/>

Demo

Edit objects-are-not-valid-as-a-react-child-using-datepicker