Search code examples
reactjsmodal-dialogbootstrap-modal

Modal with reacjs is getting null value


I have a parent like this:

function ViewBudgetPage() {
  const pathname = useParams();
  const [plate, setPlate] = useState("");
  const [bikeDataModal, setBikeDataModal] = useState(false);

  useEffect(() => {
    getBudgetByIdRequest(pathname.id).then((response) => {
      setPlate(response.data.plate);
    });
  }, [pathname.id]);

  function openBikeDataModal() {
    setBikeDataModal(true);
  }

  function closeBikeDataModal() {
    setBikeDataModal(false);
  }

  return (
    <div className="text-center mt-5">
        <input type="text" defaultValue={plate} disabled className="me-3" />
        <button className="btn btn-outline-primary" onClick={openBikeDataModal}>
          Click Me
        </button>
      <BikeDataModal
        show={bikeDataModal}
        close={closeBikeDataModal}
        plate={plate}
      />
    </div>
  );
}
export default ViewBudgetPage;

I get the plate data in the useEffect and set it to the value.
The plate value is being passed to the modal.
The modal open only on click.
Inside the modal class I have another useEffect that uses the plate value in a backend request.
When first rendering the plate value is null and I get error from backend, after that I get the right values.
enter image description here How to make my modal get the right plate value?


Solution

  • I am assuming that you are calling GET /listBikeByPlate endpoint in BikeDataModal component.

    If that is the case, the problem is that you are rendering the BikeDataModal when plate is still undefined/null, consequently calling GET /listBikeByPlate with no value for your query parameter plate (according to the ?plate= in screenshot)

    So the best thing you can do is to avoid rendering the child component until the initial request in useEffect has been finished.

    My suggestion is to add a new state like so

    const [isLoaded, setIsLoaded] = useState(false)
    

    And then inside of your getBudgetByIdRequest, set setIsFetching to true once the fetch is resolved/finished like so

      useEffect(() => {
        getBudgetByIdRequest(pathname.id).then((response) => {
          setPlate(response.data.plate);
          setIsLoaded(true)
        });
      }, [pathname.id]);
    

    And finally do the conditional rendering right before your original return block

    if (!isLoaded) {
      return <>Loading</>
    }
    

    So after the fetch is finished and your plate has been set, your BikeDataModal will now have the correct plate data, making the correct API call.

    In conclusion, you full code should be

    function ViewBudgetPage() {
        const pathname = useParams()
        const [plate, setPlate] = useState('')
        const [bikeDataModal, setBikeDataModal] = useState(false)
        const [isLoaded, setIsLoaded] = useState(false)
    
        useEffect(() => {
            getBudgetByIdRequest(pathname.id).then((response) => {
                setPlate(response.data.plate)
                setIsLoaded(true)
            })
        }, [pathname.id])
    
        function openBikeDataModal() {
            setBikeDataModal(true)
        }
    
        function closeBikeDataModal() {
            setBikeDataModal(false)
        }
    
        if (!isLoaded) return <>Loading</>
    
        return (
            <div className="text-center mt-5">
                <input type="text" defaultValue={plate} disabled className="me-3" />
                <button className="btn btn-outline-primary" onClick={openBikeDataModal}>
                    Click Me
                </button>
                <BikeDataModal show={bikeDataModal} close={closeBikeDataModal} plate={plate} />
            </div>
        )
    }
    export default ViewBudgetPage