繁体   English   中英

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

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

我正在尝试呈现一个多页表单以根据带有 switch 语句的step变量显示特定页面。 令我沮丧的是,在呈现 switch 语句的调用之后没有任何结果是我遗漏了什么吗?

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;

下面的组件如下:

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;

最后一个组件是:

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;

我不是 100% 确定这个建议的有效性,所以其他人应该权衡。但为了实现你想要的,你需要从你的jsx返回一个renderSongDetailPage而不是 jsx 并将道具作为 object 传递给成分。 然后将renderSongDetailPage作为jsx元素调用。 请记住, renderSongDetailPage需要大写的RenderSongDetailPage 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,
      });
  }
};

用法:

<RenderSongDetailPage/>

如果我可以通过观察提出建议。 RenderSongDetailPage groupedSongMetaProperties中唯一发生变化的是RenderSongDetailPage的数组项,因此您可以按如下方式在主返回中进行渲染:

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

这应该会给你相同的结果。 虽然我不知道你项目的全部范围所以只是一个建议。

最终你在野外看到的只是一堆if语句,每个语句返回一个SongDetailsPage

一个代码沙箱: https://codesandbox.io/s/jolly-gagarin-n66xp9?file=/src/App.js

问题出在 SongDetailsPage 组件中。 我没有在每个 map 迭代中返回。

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;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM