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

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

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?

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

The Component below that is as follows:

  1. 下面的组件如下

And the last component is:

  1. 最后一个组件如下
英文:

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?

  1. import React, { useState } from "react";
  2. import { songValidationSchema } from "../../../../utilities/validation";
  3. import { Form } from "../../../../utilities/components/form";
  4. import SONG_CAPTURE_PROPERTIES from "../../../../utilities/constants/SongCaptureProperties";
  5. import { groupSongMetaProperties } from "../../../../utilities/functions";
  6. import SongDetailsPage from "./SongDetailsPage";
  7. import "./SongUpload.css";
  8. const SongUpload = () => {
  9. const [step, setStep] = useState(1);
  10. const groupedSongMetaProperties = groupSongMetaProperties(
  11. SONG_CAPTURE_PROPERTIES,
  12. 4 // Into arrays of max length of 4.
  13. );
  14. const initialValues = {
  15. song: "",
  16. art: "",
  17. trackName: "",
  18. genre: "",
  19. album: "",
  20. artist: "",
  21. publisher: "",
  22. recordLabel: "",
  23. releaseYear: "",
  24. copyrightMessage: "",
  25. subtittle: "",
  26. comment: "",
  27. unsynchronisedLyric: "",
  28. };
  29. const nextStep = () => setStep(step + 1);
  30. const previousStep = () => setStep(step - 1);
  31. const handleSubmit = (values) => console.log("Form Data: ", values);
  32. const renderSongDetailPage = () => {
  33. switch (step) {
  34. case 1:
  35. return (
  36. <SongDetailsPage
  37. inputNames={groupedSongMetaProperties[0]}
  38. nextStep={nextStep}
  39. previousStep={previousStep}
  40. />
  41. );
  42. case 2:
  43. return (
  44. <SongDetailsPage
  45. inputNames={groupedSongMetaProperties[1]}
  46. nextStep={nextStep}
  47. previousStep={previousStep}
  48. />
  49. );
  50. case 3:
  51. return (
  52. <SongDetailsPage
  53. inputNames={groupedSongMetaProperties[2]}
  54. nextStep={nextStep}
  55. previousStep={previousStep}
  56. />
  57. );
  58. case 4:
  59. return (
  60. <SongDetailsPage
  61. inputNames={groupedSongMetaProperties[3]}
  62. nextStep={nextStep}
  63. previousStep={previousStep}
  64. />
  65. );
  66. }
  67. };
  68. return (
  69. <Form
  70. initialValues={initialValues}
  71. onSubmit={handleSubmit}
  72. validationSchema={songValidationSchema}
  73. >
  74. <h1>SongUpload</h1>
  75. <h3>Upload Your Songs here!</h3>
  76. {renderSongDetailPage() // This is the function calling the switch Render.}
  77. </Form>
  78. );
  79. };
  80. export default SongUpload;

The Component below that is as follows:

  1. import React from "react";
  2. import { FormInput } from "../../../common/forms/form";
  3. const SongDetailsPage = ({ inputNames, nextStep, previousStep, step }) => {
  4. const getInputType = (str, step) => {
  5. let type = "";
  6. switch (step) {
  7. case 1:
  8. switch (str) {
  9. case "song_file":
  10. type = "file";
  11. break;
  12. case "song_art":
  13. type = "image";
  14. break;
  15. case "track_name":
  16. type = "text";
  17. break;
  18. case "genre":
  19. type = "text";
  20. break;
  21. default:
  22. type = null;
  23. break;
  24. }
  25. break;
  26. case 2:
  27. type = "text"; // Since all fields need text in this group
  28. break;
  29. case 3:
  30. switch (str) {
  31. case "release_year":
  32. type = "date";
  33. break;
  34. case "subtittle":
  35. type = "text";
  36. break;
  37. default:
  38. type = null;
  39. break;
  40. }
  41. break;
  42. case 4:
  43. // case "unsynchronised_lyric": -> The only case in the last one
  44. type = null;
  45. }
  46. return type;
  47. };
  48. if (inputNames == null) return;
  49. else
  50. return (
  51. <span className="input-wrapper">
  52. {inputNames.map((key, index) => {
  53. const type = getInputType(inputNames[index], step);
  54. if (type !== null)
  55. <span key={key}>
  56. <FormInput
  57. label={inputNames[index]}
  58. name={
  59. inputNames[index] === "song_file"
  60. ? "song"
  61. : inputNames[index] === "song_art"
  62. ? "art"
  63. : inputNames[index] === "track_name"
  64. ? "trackName"
  65. : inputNames[index]
  66. }
  67. type={type}
  68. />
  69. ;
  70. </span>;
  71. else <span key={key}> Yo! </span>;
  72. })}
  73. </span>
  74. );
  75. };
  76. export default SongDetailsPage;

And the last component is:

  1. import { faEye, faEyeLowVision } from "@fortawesome/free-solid-svg-icons";
  2. import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
  3. import React, { useEffect, useRef, useState } from "react";
  4. import "./Form.css";
  5. const FormInput = ({
  6. accept,
  7. icon,
  8. id,
  9. label,
  10. onBlur,
  11. onChange,
  12. name,
  13. type,
  14. ...otherProps
  15. }) => {
  16. const [passwordRevealed, setPasswordRevealed] = useState(false);
  17. const inputElement = useRef(null);
  18. const labelElement = useRef(null);
  19. useEffect(() => {
  20. // input-in-focus css class moves the label out of the field and shrinks it
  21. if (inputElement.current.value !== "") {
  22. labelElement.current.classList.add("input-in-focus");
  23. }
  24. });
  25. const handleRevealPassword = () => {
  26. setPasswordRevealed(!passwordRevealed);
  27. if (!passwordRevealed) type = "text";
  28. else type = "password";
  29. inputElement.current.type = type;
  30. };
  31. const handleFocus = () => {
  32. if (
  33. document.activeElement === inputElement.current ||
  34. inputElement.current.value !== ""
  35. )
  36. labelElement.current.classList.add("input-in-focus");
  37. else labelElement.current.classList.remove("input-in-focus");
  38. };
  39. const handleBlur = (e) => {
  40. if (e.target.type === "file") return;
  41. handleFocus();
  42. onBlur(e);
  43. };
  44. return (
  45. <div className="form-input-container">
  46. {icon && (
  47. <span className="label-icon-container">
  48. <FontAwesomeIcon
  49. className="form-input-icon"
  50. // color={defaultStyles.colors.tomato}
  51. icon={icon}
  52. size="lg"
  53. />
  54. </span>
  55. )}
  56. <label>
  57. <p ref={labelElement}>{label}</p>
  58. <input
  59. accept={accept}
  60. id={id}
  61. name={name}
  62. onFocus={handleFocus}
  63. onBlur={handleBlur}
  64. onChange={onChange}
  65. ref={inputElement}
  66. type={type}
  67. {...otherProps}
  68. />
  69. </label>
  70. {type === "password" && (
  71. <span className="upendi-password-eye">
  72. <FontAwesomeIcon
  73. className="password-eye"
  74. // color={defaultStyles.colors.tomato}
  75. icon={passwordRevealed ? faEyeLowVision : faEye}
  76. onClick={handleRevealPassword}
  77. size="lg"
  78. title={
  79. passwordRevealed
  80. ? "Click To Hide Password"
  81. : "Click To Reveal Password"
  82. }
  83. />
  84. </span>
  85. )}
  86. </div>
  87. );
  88. };
  89. 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.

  1. const RenderSongDetailPage = () => {
  2. switch (step) {
  3. case 1:
  4. return SongDetailsPage({
  5. inputNames: groupedSongMetaProperties[0],
  6. nextStep: nextStep,
  7. previousStep: previousStep,
  8. });
  9. case 2:
  10. return SongDetailsPage({
  11. inputNames: groupedSongMetaProperties[1],
  12. nextStep: nextStep,
  13. previousStep: previousStep,
  14. });
  15. case 3:
  16. return SongDetailsPage({
  17. inputNames: groupedSongMetaProperties[2],
  18. nextStep: nextStep,
  19. previousStep: previousStep,
  20. });
  21. case 4:
  22. return SongDetailsPage({
  23. inputNames: groupedSongMetaProperties[3],
  24. nextStep: nextStep,
  25. previousStep: previousStep,
  26. });
  27. }
  28. };

Usage :

  1. <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 :

  1. <SongDetailsPage
  2. inputNames={groupedSongMetaProperties[step-1]}
  3. nextStep={nextStep}
  4. previousStep={previousStep}
  5. />;

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.

  1. const RenderSongDetailPage = () =&gt; {
  2. switch (step) {
  3. case 1:
  4. return SongDetailsPage({
  5. inputNames: groupedSongMetaProperties[0],
  6. nextStep: nextStep,
  7. previousStep: previousStep,
  8. });
  9. case 2:
  10. return SongDetailsPage({
  11. inputNames: groupedSongMetaProperties[1],
  12. nextStep: nextStep,
  13. previousStep: previousStep,
  14. });
  15. case 3:
  16. return SongDetailsPage({
  17. inputNames: groupedSongMetaProperties[2],
  18. nextStep: nextStep,
  19. previousStep: previousStep,
  20. });
  21. case 4:
  22. return SongDetailsPage({
  23. inputNames: groupedSongMetaProperties[3],
  24. nextStep: nextStep,
  25. previousStep: previousStep,
  26. });
  27. }
  28. };

Usage :

  1. &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 :

  1. &lt;SongDetailsPage
  2. inputNames={groupedSongMetaProperties[step-1]}
  3. nextStep={nextStep}
  4. previousStep={previousStep}
  5. /&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.

  1. import {
  2. faCalendar,
  3. faComment,
  4. faCompactDisc,
  5. faCopyright,
  6. faFileAudio,
  7. faFileWord,
  8. faGuitar,
  9. faImage,
  10. faMusic,
  11. faNewspaper,
  12. faPenToSquare,
  13. faRecordVinyl,
  14. faUser,
  15. } from &quot;@fortawesome/free-solid-svg-icons&quot;;
  16. import React from &quot;react&quot;;
  17. import { FormInput } from &quot;../../../../utilities/components/form&quot;;
  18. const SongDetailsPage = ({ inputNames, step }) =&gt; {
  19. const getInputType = (str, step) =&gt; {
  20. switch (step) {
  21. case 1:
  22. switch (str) {
  23. case &quot;song_file&quot;:
  24. return &quot;file&quot;;
  25. case &quot;song_art&quot;:
  26. return &quot;image&quot;;
  27. default:
  28. return &quot;text&quot;;
  29. }
  30. case 2:
  31. return &quot;text&quot;; // Since all fields need text in this group
  32. case 3:
  33. switch (str) {
  34. case &quot;release_year&quot;:
  35. return &quot;date&quot;;
  36. case &quot;subtitle&quot;:
  37. return &quot;text&quot;;
  38. default:
  39. return null;
  40. }
  41. case 4:
  42. // case &quot;unsynchronised_lyric&quot;: -&gt; The only case in the last one
  43. return null;
  44. }
  45. };
  46. if (!inputNames) return;
  47. else
  48. return (
  49. &lt;&gt;
  50. {inputNames.map((key, index) =&gt; {
  51. const type = getInputType(inputNames[index], step);
  52. return ( // &lt;- This return statement was the issue.
  53. ...
  54. );
  55. })}
  56. &lt;/&gt;
  57. );
  58. };
  59. 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:

确定