如何使用switch语句有条件地渲染React函数组件?

huangapple go评论145阅读模式
英文:

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?

我正在尝试使用switch语句基于`step`变量来渲染多页表单但令我沮丧的是在渲染switch语句后没有任何内容出现我是否漏掉了什么

The Component below that is as follows:

下面的组件如下

And the last component is:

最后一个组件如下
英文:

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;

答案1

得分: 2

I'm not 100% sure about the validity of this suggestion so others should weigh in. But in order to achieve what you want, you will need to return a function instead of jsx from your renderSongDetailPage and pass the props as an object to the component. Then call renderSongDetailPage as a jsx element. Remember that renderSongDetailPage needs to RenderSongDetailPage with a capital R.

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,
      });
  }
};

Usage :

<RenderSongDetailPage/>

If I can make a suggestion through observation. The only thing changing in that RenderSongDetailPage function is the array item for groupedSongMetaProperties so you could possible just render in the main return as follows :

<SongDetailsPage
  inputNames={groupedSongMetaProperties[step-1]}
  nextStep={nextStep}
  previousStep={previousStep}
/>;

which should give you the same results. Although I do not know the entire extent of your project so just a suggestion.

Ultimately what you would see in the wild is just a bunch of if statements each returning a SongDetailsPage

A code sandbox : https://codesandbox.io/s/jolly-gagarin-n66xp9?file=/src/App.js

英文:

I'm not 100% sure about the validity of this suggestion so others should weigh in. But in order to achieve what you want, you will need to return a function instead of jsx from your renderSongDetailPage and pass the props as an object to the component. Then call renderSongDetailPage as a jsx element. Remember that renderSongDetailPage needs to RenderSongDetailPage with a capital R.

const RenderSongDetailPage = () =&gt; {
  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,
      });
  }
};

Usage :

&lt;RenderSongDetailPage/&gt;

If I can make a suggestion through observation. The only thing changing in that RenderSongDetailPage function is the array item for groupedSongMetaProperties so you could possible just render in the main return as follows :

&lt;SongDetailsPage
  inputNames={groupedSongMetaProperties[step-1]}
  nextStep={nextStep}
  previousStep={previousStep}
/&gt;;

which should give you the same results. Although I do not know the entire extent of your project so just a suggestion.

Ultimately what you would see in the wild is just a bunch of if statements each returning a SongDetailsPage

A code sandbox : https://codesandbox.io/s/jolly-gagarin-n66xp9?file=/src/App.js

答案2

得分: 0

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

英文:

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 &quot;@fortawesome/free-solid-svg-icons&quot;;
import React from &quot;react&quot;;

import { FormInput } from &quot;../../../../utilities/components/form&quot;;

const SongDetailsPage = ({ inputNames, step }) =&gt; {
  const getInputType = (str, step) =&gt; {
    switch (step) {
      case 1:
        switch (str) {
          case &quot;song_file&quot;:
            return &quot;file&quot;;

          case &quot;song_art&quot;:
            return &quot;image&quot;;

          default:
            return &quot;text&quot;;
        }

      case 2:
        return &quot;text&quot;; // Since all fields need text in this group

      case 3:
        switch (str) {
          case &quot;release_year&quot;:
            return &quot;date&quot;;

          case &quot;subtitle&quot;:
            return &quot;text&quot;;

          default:
            return null;
        }
      case 4:
        // case &quot;unsynchronised_lyric&quot;: -&gt; The only case in the last one
        return null;
    }
  };

  if (!inputNames) return;
  else
    return (
      &lt;&gt;
        {inputNames.map((key, index) =&gt; {
          const type = getInputType(inputNames[index], step);
          return ( // &lt;- This return statement was the issue.
            ...
          );
        })}
      &lt;/&gt;
    );
};

export default SongDetailsPage;

huangapple
  • 本文由 发表于 2023年1月4日 09:38:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75000201.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定