React date input state management

Viewed 36

I'm working on a project ( Airbnb clone - for personal learning), and on this project I am trying to understand concepts and conventions thoroughly.

Here's the problem:

enter image description here

On this page I use a date input custom component like this: -- parent component -

import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { testData } from "../utils/mockData";

import Header from "../components/header/Header";
import DateSearchBar from "../components/header/DateSearchBar";
import Guests from "../components/cards/Guests";

const Property = () => {
  const propertyId = useParams();
  const convertedId = parseInt(propertyId.id);
  const [noOfGuests, setNoOfGuests] = useState(0);
  const [fromDate, setFromDate] = useState();
  const [toDate, setToDate] = useState();

  const selectedProperty = testData.filter(item => item.id === propertyId.id)

  const handleGuests = (val) => {
    if (noOfGuests === 0) {
      setNoOfGuests(0);
    }
    setNoOfGuests(noOfGuests + val)
    
  }
  const handleDate = (val) => {
    setFromDate(val);
}
  

  return (
    <div>
      <Header />

      <div className="flex justify-center space-x-24 mt-6">
        <div className=" max-w-sm  rounded-xl overflow-hidden shadow-sm w-9/12">
          <img
            className=" text-centerw w-96  rounded-md h-64"
            src={selectedProperty[0].image}
          />
          <p className="h-16">{selectedProperty[0].title}</p>
        </div>

        <div className="ml-96">
          <h4 className="text-center italic font-extrabold">From</h4>
          <DateSearchBar name="fromDate" value={fromDate} handleDate={handleDate }  />
          <h4 className="text-center italic font-extrabold">To</h4>
          <DateSearchBar name="toDate" value={fromDate} handleDate={handleDate }  />
          <Guests handleGuests={handleGuests} noOfGuests={noOfGuests} />
        </div>
      </div>
    </div>
  );
};

export default Property;

--- Child Component ---

import React from "react";

const DateSearchBar = ({ handleDate }) => {
  return (
    <div>
      {/* fromDate */}
      <div className="text-center">
        <input
          className="bg-slate-50 hover:bg-red-200 rounded-md h-12 w-80  text-center mb-16 "
          type="date"
          onChange={(e) => handleDate(e.target.value)}
        />
      </div>
    </div>
  );
};

export default DateSearchBar;

The Property.js component owns the local state, I am using a callback function to set a local state in the parent component. The problem is that I need to differ between the fromDate state and the toDate state in the parent component, but I'm not sure how to write this logic.

Obviously I can set up another date component and target it, however it beats the purpose of creating and using reusable components and keeping your code DRY.

Also, Redux/Context seem too much for this issue ( but I might be wrong)

Any ideas on how I can solve this ?

1 Answers

First thing is, that you are passing fromDate in both of the components. I believe you should have a value of fromDate in the first and toDate in the second.

Next, to be able to reuse handleDate for both inputs, you need to pass the element's name back to the parent along with the value and then use that name argument to differentiate between the two components.

In parent:

const handleDate = (name, value) => {
   if (name === "fromDate") {
      setFromDate(value)
   } else if (name === "toDate") {
      setToDate(value)
   }
}

In child:

onChange={e => handleDate(props.name, e.target.value)}

Another approach would be to return a method from handleDate():

In parent:

const handleDate = (name) => {
   if (name === "fromDate") {
      return (value) => setFromDate(value)
   } else if (name === "toDate") {
      return (value) => setToDate(value)
   }
}
...
<DateSearchBar name="fromDate" value={fromDate} handleDate={handleDate("fromDate")}  />
<DateSearchBar name="toDate" value={toDate} handleDate={handleDate("toDate")}  />

In this case, you don't have to change child component.

However IMO this still isn't the simplest approach. Yes, we should try to follow these clean code recommendations but only to the point where they don't lead to further complexity. For example, in the above case we are over-complicating handleDate(), it would be a lot simpler to have separate inline change handlers for each component:

<DateSearchBar name="fromDate" value={fromDate} handleDate={val => setFromDate(val)}  />
<DateSearchBar name="toDate" value={toDate} handleDate={val => setToDate(val)}  />

If our form grows bigger, we can use dedicated form handling React libraries such as Formik and React-hook-form to keep our component logic simpler.

Related