@kanfutrooper (masaomi)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Basics、Question、Optionalの回答をConfirm.jsで表示したい。

Q&A

Closed

ReactでMaterial-UI を使用して、Web フォームを作成しています。

Basics、Question、Optionalの回答をConfirm.jsで表示したい。

Basics、Question、Optionalのページは表示されるのだが、Confirm.jsのぺージを開こうとしたら下記のエラーが表示される。

TypeError: Cannot read properties of undefined (reading 'filter')
Questionnaire
src/components/Questionnaire.js:21

  18 | };
  19 | return (
  20 |   <div style={{ textAlign: "center" }}>
> 21 |     <FormControl component="fieldset">
  22 | ^      {answers
  23 |         .filter((_, i) => i === 0 || answers[i - 1])
  24 |         .map((answer, i) => (

期待する動作
①上記のエラーを解消したい。
②Basics、Question、Optionalの回答をConfirm.jsで表示したい。

問題解決のため行ったこと
前回の質問をした際にアドバイスを参考にしながら、Confirm.jsを作成したがエラーは改善しなかった。

src / components / contents.js

import React from "react";
import { Grid } from "@mui/material";
import Stepper from "@mui/material/Stepper";
import Step from "@mui/material/Step";
import StepLabel from "@mui/material/StepLabel";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import Basic from "./Basic";
import Questionnaire, { QUESTIONS } from "./Questionnaire";
import Optional from "./Optional";
import Confirm from "./Confirm";

function getSteps() {
  return ["お客様の情報を入力して下さい", "以下にお答え下さい", "ご相談下さい", "以下の内容をご確認下さい"];
}
const StepContent = ({ stepIndex, questionnaireProps }) => {
  switch (stepIndex) {
    case 0:
      return <Basic />;
case 1:
      return <Questionnaire {...questionnaireProps} />;
    case 2:
      return <Optional />;
case 3:
      return <Confirm />;
    default:
      return "Unknown stepIndex";
  }
};
function Content() {
  const [activeStep, setActiveStep] = React.useState(0);
  const [answers, setAnswers] = React.useState(Array(QUESTIONS.length).fill(null));
  const steps = getSteps();
  const handleNext = () => {
    setActiveStep((prevActiveStep) => prevActiveStep + 1);
  };
const handleBack = () => {
    setActiveStep((prevActiveStep) => prevActiveStep - 1);
  };
  const handleReset = () => {
    setActiveStep(0);
  };
  const buttonDisabled = activeStep === 1 && answers.some((a) => !a);
  return (
    <Grid container>
      <Grid sm={2} />
      <Grid lg={8} sm={8} spacing={10}>
        <Stepper activeStep={activeStep} alternativeLabel>
          {steps.map((label) => (
            <Step key={label}>
              <StepLabel>{label}</StepLabel>
            </Step>
          ))}
        </Stepper>
        {activeStep === steps.length ? (
          <div>
            <Typography>全ステップの表示を完了</Typography>
            <Button onClick={handleReset}>リセット</Button>
          </div>
        ) : (
          <div>
            <Typography>
              <StepContent stepIndex={activeStep} questionnaireProps={{ answers, setAnswers }} />
            </Typography>
            <Button disabled={activeStep === 0} onClick={handleBack}>
              戻る
            </Button>
            <Button variant="contained" color="primary" onClick={handleNext} disabled={buttonDisabled}>
              {activeStep === steps.length - 1 ? "送信" : "次へ"}
            </Button>
          </div>
        )}
      </Grid>
    </Grid>
  );
}
export default Content;

src / components / Basics.js

import React from "react";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import FormLabel from "@mui/material/FormLabel";
import InputLabel from "@mui/material/InputLabel";
import Select from "@mui/material/Select";

const Basic = () => {
  return (
    <>
      <div style={{ textAlign: "center" }}>
        <FormControl component="fieldset">
          <FormLabel component="legend">- 性別 -</FormLabel>
          <RadioGroup row aria-label="gender" name="row-radio-buttons-group">
            <FormControlLabel defaultValue="male" control={<Radio />} label="男性" />
            <FormControlLabel defaultValue="female" control={<Radio />} label="女性" />
          </RadioGroup>
        </FormControl>
      </div>
      <div style={{ textAlign: "center" }}>
        <FormLabel component="legend">- 生年月日 -</FormLabel>
        <FormControl sx={{ m: 1, minWidth: 120 }}>
          <InputLabel htmlFor="grouped-native-select">year</InputLabel>
          <Select native defaultValue="" id="grouped-native-select" label="Grouping">
            <option aria-label="None" value="" />
            <optgroup label="year">
              {Array.from(Array(2020), (_, num) => (
                <option key={num} value={num + 1}>
                  {num + 1990}
                </option>
              ))}
            </optgroup>
          </Select>
        </FormControl>
        <FormControl sx={{ m: 1, minWidth: 120 }}>
          <InputLabel htmlFor="grouped-native-select">month</InputLabel>
          <Select native defaultValue="" id="grouped-native-select" label="Grouping">
            <option aria-label="None" value="" />
            <optgroup label="month">
              {Array.from(Array(12), (_, num) => (
                <option key={num} value={num + 1}>
                  {num + 1}
                </option>
              ))}
            </optgroup>
          </Select>
        </FormControl>
        <FormControl sx={{ m: 1, minWidth: 120 }}>
          <InputLabel htmlFor="grouped-native-select">day</InputLabel>
          <Select native defaultValue="" id="grouped-native-select" label="Grouping">
            <option aria-label="None" value="" />
            <optgroup label="day">
              {Array.from(Array(12), (_, num) => (
                <option key={num} value={num + 1}>
                  {num + 1}
                </option>
              ))}
            </optgroup>
          </Select>
        </FormControl>
      </div>
    </>
  );
};

export default Basic;

src / components / Questionnaire.js

import React from "react";
import Typography from "@mui/material/Typography";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormControl from "@mui/material/FormControl";
import FormLabel from "@mui/material/FormLabel";

export const QUESTIONS = [
  "現在、生命保険に加入されていますか?",
  "現在、入院中ですか。また、3ヶ月以内に医師の診察・検査の結果、入院・手術をすすめられたことがありますか?",
  "過去、5年以内に病気やケガで手術を受けたことまたは継続して7日以上の入院をしたことはありますか?",
];

const Questionnaire = ({ answers, setAnswers }) => {
  const handleAnswer = (answeredIndex, answer) => {
    setAnswers(answers.map((e, i) => (i === answeredIndex ? answer : e)));
  };
  return (
    <div style={{ textAlign: "center" }}>
      <FormControl component="fieldset">
        {answers
          .filter((_, i) => i === 0 || answers[i - 1])
          .map((answer, i) => (
            <React.Fragment key={i}>
              <FormLabel component="legend">{QUESTIONS[i]}</FormLabel>
              {answer ? (
                <Typography>{answer === "yes" ? "はい" : "いいえ"}</Typography>
              ) : (
                <RadioGroup
                  row
                  aria-label="gender"
                  name="row-radio-buttons-group"
                  onChange={(_evt, value) => {
                    handleAnswer(i, value);
                  }}
                >
                  <FormControlLabel value="yes" control={<Radio />} label="はい" />
                  <FormControlLabel value="no" control={<Radio />} label="いいえ" />
                </RadioGroup>
              )}
            </React.Fragment>
          ))}
      </FormControl>
    </div>
  );
};

export default Questionnaire;

src / components / Optional.js

import React from "react";
import { Grid } from "@mui/material";
import Tooltip from "@mui/material/Tooltip";
import TextField from "@mui/material/TextField";

const Optional = () => {
  return (
    <div>
      <Grid container>
        <Grid sm={2} />
        <Grid lg={8} sm={8} spacing={10}>
          <Tooltip title="ご相談内容を記入することができます" placement="top-start" arrow>
            <TextField
              label="ご相談内容"
              fullWidth
              margin="normal"
              rows={4}
              multiline
              variant="outlined"
              placeholder="その他ご要望等あれば、ご記入ください"
            />
          </Tooltip>
        </Grid>
      </Grid>
    </div>
  );
};
export default Optional;

src / components / Confirm.js

mport React from "react";
import Basic from "./Basic";
import Questionnaire from "./Questionnaire";
import Optional from "./Optional";

export const UserInputData = React.createContext();

function Confirm() {
  const [currentState, setCurrentState] = React.useState({});
  const value = {
    currentState,
    setCurrentState,
  };
  return (
    <div style={{ textAlign: "center" }}>
      <Basic />
      <Questionnaire />
      <Optional />
    </div>
  );
}

export default Confirm;
0 likes

1Answer

エラーメッセージはそれで合っていますか?

> 21 |     <FormControl component="fieldset">
     | ^  22 |       {answers

> 21 |     <FormControl component="fieldset">
     | ^ (エラーメッセージ)
  22 |       {answers

となっているべきだと思いますが。また実際の src/components/Questionnaire.js 21行目付近の内容とも食い違いがあります。

0Like

Comments

  1. @kanfutrooper

    Questioner

    @uasiさん、ご指摘ありがとうございます!
    自身の記載ミスです。再度エラー箇所を編集をし直しました。
  2. 対応ありがとうございます。

    エラーは undefined が入った変数 answers に対して filter メソッドを呼び出しそうとしていることを表します。

    その原因は Basics.js で <Questionnaire /> を使っているからです。 Questionnaire コンポーネントの answers prop に何も値を渡していないので、 undefined が入ります。適切な配列を渡してください。
  3. @kanfutrooper

    Questioner

    @uasiさん、解説ありがとうございます!
    『Questionnarie コンポーネントの answers prop に何も値を渡していないので、 undefined が入ります。適切な配列を渡してください。』  ⇨ 申し訳ないのですが、answers prop に値を渡す配列を教えて下さい。

  4. 可能か可能でないか以前に React の理解度が不足していると思います。今のエラー、つまり Questionnaire に適切な props を渡せていないこと、が解決できないうちは useContext を使っても混乱するだけです。

    もし useContext を使いたいのであれば、

    - 「useContext を使って〇〇から××に△△を渡したい」と(具体的な名詞を入れて)説明できること
    - props ではなく useContext を使う理由は何かを説明できること

    の2点をクリアしてからにすべきです。
  5. ちなみに、解答例は「useContext を使って Confirm コンポーネントから Questionnarie コンポーネントに answers を渡したい」です。それで言えば可能ですが、この場合は props ではなく useContext を使う積極的な理由がないので使わないほうがいいです。
  6. ところで上の方のコメントで

    > その原因は Basics.js で <Questionnaire /> を使っているからです。

    と書きましたが、私の見間違いか質問文を修正されたのか分かりませんけども、正しくは Confirm.js です。

    可能か可能でないか……についても修正前のコメントに返答していたようです。重ね重ね失礼しました。
  7. @kanfutrooper

    Questioner

    @uasiさん、丁寧な回答及び解説、ありがとうございます!
    Webフォームを作成する際、検索して『useContext』という機能を使用すれば、
    完成できるのではと自分なりに仮説を立てて実装を行なっていました。
  8. > 『useContext』という機能を使用すれば、完成できるのでは

    使うことはできますが、この場合は使わない方が簡単でシンプルなんじゃないかと思います。多数のコンポーネントで同じ state を共有したくなったとき、あるいは深い階層のコンポーネントに state を渡したいときなど、 props 経由だと煩雑になりそうな場合に検討してください。

    > 申し訳ないのですが、answers prop に値を渡す配列を教えて下さい。

    まずご自分のコードが何をやっているのかきちんと理解して、分からなければ JavaScript と React の復習をしたり、もっと小規模な10行くらいのコンポーネントを書いて動作確認したりした方がいいですよ。

    answers には質問の数だけ null が入った配列を初期値として渡す必要があります。同様のコードはすでに contents.js で書かれていますね。

    const [answers, setAnswers] = React.useState(Array(QUESTIONS.length).fill(null));

    これを Confirm.js でも書いて、

    <Questionnaire answers={answers} setAnswers={setAnswers} />

    と渡せばいいはずです。(全体に目を通していないのでこれで動くかどうか確信は持てませんが)
  9. @kanfutrooper

    Questioner

    @uasiさん、丁寧な回答及び解説、ありがとうございます!
    Confirm.js に、Basics、Question、Optionalの『質問』は表示されるようになりました!
    色々アドバイス、ありがとうございました!

Your answer might help someone💌