Search code examples
javascriptnode.jsreactjsreact-hooksreact-functional-component

How do I conditionally render react functional components with switch statement?


I am trying to render a multipage form to display a particular page based on the step variable with a switch statement. To my dismay, nothing is coming out after the call that renders the switch statement am I missing something?

import React, { useState } from "react";

import { songValidationSchema } from "../../../../utilities/validation";
import { Form } from "../../../../utilities/components/form";
import SONG_CAPTURE_PROPERTIES from "../../../../utilities/constants/SongCaptureProperties";
import { groupSongMetaProperties } from "../../../../utilities/functions";
import SongDetailsPage from "./SongDetailsPage";

import "./SongUpload.css";

const SongUpload = () => {
  const [step, setStep] = useState(1);

  const groupedSongMetaProperties = groupSongMetaProperties(
    SONG_CAPTURE_PROPERTIES,
    4 // Into arrays of max length of 4.
  );

  const initialValues = {
    song: "",
    art: "",
    trackName: "",
    genre: "",
    album: "",
    artist: "",
    publisher: "",
    recordLabel: "",
    releaseYear: "",
    copyrightMessage: "",
    subtittle: "",
    comment: "",
    unsynchronisedLyric: "",
  };

  const nextStep = () => setStep(step + 1);
  const previousStep = () => setStep(step - 1);

  const handleSubmit = (values) => console.log("Form Data: ", values);

  const renderSongDetailPage = () => {
    switch (step) {
      case 1:
        return (
          <SongDetailsPage
            inputNames={groupedSongMetaProperties[0]}
            nextStep={nextStep}
            previousStep={previousStep}
          />
        );
      case 2:
        return (
          <SongDetailsPage
            inputNames={groupedSongMetaProperties[1]}
            nextStep={nextStep}
            previousStep={previousStep}
          />
        );
      case 3:
        return (
          <SongDetailsPage
            inputNames={groupedSongMetaProperties[2]}
            nextStep={nextStep}
            previousStep={previousStep}
          />
        );
      case 4:
        return (
          <SongDetailsPage
            inputNames={groupedSongMetaProperties[3]}
            nextStep={nextStep}
            previousStep={previousStep}
          />
        );
    }
  };

  return (
    <Form
      initialValues={initialValues}
      onSubmit={handleSubmit}
      validationSchema={songValidationSchema}
    >
      <h1>SongUpload</h1>
      <h3>Upload Your Songs here!</h3>

      {renderSongDetailPage() // This is the function calling the switch Render.} 
    </Form>
  );
};
export default SongUpload;

The Component below that is as follows:

import React from "react";

import { FormInput } from "../../../common/forms/form";

const SongDetailsPage = ({ inputNames, nextStep, previousStep, step }) => {
  const getInputType = (str, step) => {
    let type = "";
    switch (step) {
      case 1:
        switch (str) {
          case "song_file":
            type = "file";
            break;
          case "song_art":
            type = "image";
            break;
          case "track_name":
            type = "text";
            break;
          case "genre":
            type = "text";
            break;
          default:
            type = null;
            break;
        }
        break;
      case 2:
        type = "text"; // Since all fields need text in this group
        break;
      case 3:
        switch (str) {
          case "release_year":
            type = "date";
            break;
          case "subtittle":
            type = "text";
            break;
          default:
            type = null;
            break;
        }
        break;
      case 4:
        // case "unsynchronised_lyric": -> The only case in the last one
        type = null;
    }

    return type;
  };

  if (inputNames == null) return;
  else
    return (
      <span className="input-wrapper">
        {inputNames.map((key, index) => {
          const type = getInputType(inputNames[index], step);
          if (type !== null)
            <span key={key}>
              <FormInput
                label={inputNames[index]}
                name={
                  inputNames[index] === "song_file"
                    ? "song"
                    : inputNames[index] === "song_art"
                    ? "art"
                    : inputNames[index] === "track_name"
                    ? "trackName"
                    : inputNames[index]
                }
                type={type}
              />
              ;
            </span>;
          else <span key={key}> Yo! </span>;
        })}
      </span>
    );
};

export default SongDetailsPage;

And the last component is:

import { faEye, faEyeLowVision } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, { useEffect, useRef, useState } from "react";

import "./Form.css";

const FormInput = ({
  accept,
  icon,
  id,
  label,
  onBlur,
  onChange,
  name,
  type,
  ...otherProps
}) => {
  const [passwordRevealed, setPasswordRevealed] = useState(false);

  const inputElement = useRef(null);
  const labelElement = useRef(null);

  useEffect(() => {
    // input-in-focus css class moves the label out of the field and shrinks it
    if (inputElement.current.value !== "") {
      labelElement.current.classList.add("input-in-focus");
    }
  });

  const handleRevealPassword = () => {
    setPasswordRevealed(!passwordRevealed);
    if (!passwordRevealed) type = "text";
    else type = "password";
    inputElement.current.type = type;
  };

  const handleFocus = () => {
    if (
      document.activeElement === inputElement.current ||
      inputElement.current.value !== ""
    )
      labelElement.current.classList.add("input-in-focus");
    else labelElement.current.classList.remove("input-in-focus");
  };

  const handleBlur = (e) => {
    if (e.target.type === "file") return;
    handleFocus();
    onBlur(e);
  };

  return (
    <div className="form-input-container">
      {icon && (
        <span className="label-icon-container">
          <FontAwesomeIcon
            className="form-input-icon"
            // color={defaultStyles.colors.tomato}
            icon={icon}
            size="lg"
          />
        </span>
      )}

      <label>
        <p ref={labelElement}>{label}</p>
        <input
          accept={accept}
          id={id}
          name={name}
          onFocus={handleFocus}
          onBlur={handleBlur}
          onChange={onChange}
          ref={inputElement}
          type={type}
          {...otherProps}
        />
      </label>

      {type === "password" && (
        <span className="upendi-password-eye">
          <FontAwesomeIcon
            className="password-eye"
            // color={defaultStyles.colors.tomato}
            icon={passwordRevealed ? faEyeLowVision : faEye}
            onClick={handleRevealPassword}
            size="lg"
            title={
              passwordRevealed
                ? "Click To Hide Password"
                : "Click To Reveal Password"
            }
          />
        </span>
      )}
    </div>
  );
};

export default FormInput;

Solution

  • The issue was in the SongDetailsPage component. I was not returning inside each map iteration.

    import {
      faCalendar,
      faComment,
      faCompactDisc,
      faCopyright,
      faFileAudio,
      faFileWord,
      faGuitar,
      faImage,
      faMusic,
      faNewspaper,
      faPenToSquare,
      faRecordVinyl,
      faUser,
    } from "@fortawesome/free-solid-svg-icons";
    import React from "react";
    
    import { FormInput } from "../../../../utilities/components/form";
    
    const SongDetailsPage = ({ inputNames, step }) => {
      const getInputType = (str, step) => {
        switch (step) {
          case 1:
            switch (str) {
              case "song_file":
                return "file";
    
              case "song_art":
                return "image";
    
              default:
                return "text";
            }
    
          case 2:
            return "text"; // Since all fields need text in this group
    
          case 3:
            switch (str) {
              case "release_year":
                return "date";
    
              case "subtitle":
                return "text";
    
              default:
                return null;
            }
          case 4:
            // case "unsynchronised_lyric": -> The only case in the last one
            return null;
        }
      };
    
      if (!inputNames) return;
      else
        return (
          <>
            {inputNames.map((key, index) => {
              const type = getInputType(inputNames[index], step);
              return ( // <- This return statement was the issue.
                ...
              );
            })}
          </>
        );
    };
    
    export default SongDetailsPage;