kanfutrooper
@kanfutrooper (masaomi)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Basic.js Optional.js の質問及び答えた回答をContent.js / case3で表示したい【2回目】

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

Basic.js Optional.js の質問及び答えた回答をContent.js case3で表示したい。

どのように実装したら良いかわからない状況で、詰まっています。 何方かアドバイスをお願いします。

期待する動作

Basic.js Optional.js の質問及び答えた回答をContent.js case3で表示したい。

問題解決のため行なったこと

① Questionnaire.jsの質問の回答はContent.js/case3の確認画面で表示できたが、Questionnaireの実装と同じようにBasic Optional の実装を
 Content.js/case3行なったが、同じように確認画面で表示できなかった。

②【1回目】でReactの基本的な使い方の理解のアドバイスを頂き、ドキュメント、過去勉強したUdemyやyoutubeなどを復習して教材上では理解できたが、
このアプリだと書き方がわからない。

src / components / Basic.js
Basic.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 value="male" control={<Radio />} label="男性" />
            <FormControlLabel value="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 / Optional.js
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 / contents.js
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";

function getSteps() {
  return ["お客様の情報を入力して下さい", "以下にお答え下さい", "ご相談下さい", "以下の内容をご確認下さい"];
}

const StepContent = ({ stepIndex, BasicProps, questionnaireProps, OptionalProps }) => {
  switch (stepIndex) {
    case 0:
      return <Basic {...BasicProps} />;
    case 1:
      return <Questionnaire {...questionnaireProps} />;
    case 2:
      return <Optional {...OptionalProps} />;
    case 3:
      return (
        <div style={{ textAlign: "center" }}>
          <Basic {...BasicProps} />
          <Questionnaire {...questionnaireProps} />
          <Optional {...OptionalProps} />
        </div>
      );
    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 style={{ textAlign: "center" }}>
            <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;
0

1Answer

回答、つまりユーザーが React コンポーネントに与えたデータを保存するには、 state (状態)として取り扱う必要があります。 ひとつの state を管理するには、 contents.js の中ですでに一度やっているように、

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

こうやって React.useState() を使って state 変数とセッター関数の組を作ります。セッター関数でデータをセットすると次にコンポーネントが描画されるときに値が state 変数に反映されます。

Questionnaire コンポーネントはこの組を props で受け取っています。

<StepContent stepIndex={activeStep} questionnaireProps={{ answers, setAnswers }} />
    case 1:
      return <Questionnaire {...questionnaireProps} />;

Questionnaire コンポーネントのソースコードは分かりませんが、回答が表示できているということは中で state を正しく扱えているのでしょう。

ひるがえって、 Basic コンポーネントは中で state を扱っていません。また渡すべき state を React.useState() で作ったり、それを props で渡したりもしていません。つまり表示する以前に回答の保存ができていないということです。

ご質問を見るに、そこに気づけていないようなので、 state を使った単純なコンポーネントを自力で書けるようになるまで復習したほうがいいと思います。

0Like

Comments

  1. @kanfutrooper

    Questioner

    @uasiさん、丁寧な解説付きの回答、ありがとうございます!
    復習のアドバイス、ありがとうございます。
    申し訳ないのですが、Basic.jsの表示できるソースコードを教えてください。
    Udemy,youtube,ドキュメントを読んだり、そのサイトでの単純なアプリは何度も行なっています。
    単純なものは書けても、実際のアプリ実装には書けないのは、経験不足ではないかと仮説を立てて、
    InputからOutputの学習に切り替えました。
    アドバイスに反して、申し訳ないのですが、Basic.jsの表示できるソースコードを教えてください。
  2. うーん、 Questionnaire コンポーネントに state を渡せているのだから、今ある知識で同じ事をやるだけで Basic にも渡せるはずで、次に詰まるとしたらラジオボタンの状態を state に保存するにはどうするかあたりだと思うのですが。

    失礼ながら私からは「10×20 + 30×40 = 1400なのは分かりました。経験不足で10×20 + 30×40 + 50×60が解けません」とおっしゃっているように見えます。ですから、経験不足ではなくかけ算の基礎が身についていないのでは、教材を復習したり単純化した50×60だけ解いたほうがいいですよ、と今まで回答していたわけです。

    単純なコンポーネントとは学習サイトではなくてご自身のアプリで余計な部分を省いたコンポーネントを書いてみて欲しいということです。 Basic コンポーネントの動かし方を作って学びたいなら、生年月日を省いて性別だけ入力させるとか、Questionnarie を削除して Basic だけ表示するなど、同じような事をやっているコードやすでに理解したコードを削って、学びたい部分にだけ集中できるコンポーネントを作ってください。

    また Basic も Optional も動かし方が分からないなら、いったん Optional は消して Basic にだけ集中してください。 Optional はあるだけ学習の邪魔ですし、動かないソースコードを大量に貼って質問されても回答側も大変です。

    いささか説教じみてきたので表示できるソースコードの話に移ります。まず

    const [basicProfile, setBasicProfile] = React.useState({ gender: null, year: null, month: null, day: null ));

    を Content コンポーネントの中に書いて

    <StepContent 略 BasicProps={{ basicProfile, setBasicProfile }} />

    のように渡してください。 Basic コンポーネント側では

    const Basic = ({ basicProfile, setBasicProfile }) => { 略 }

    とすれば受け取れます。それを

    https://mui.com/api/select/
    https://mui.com/api/radio-group/

    を参考にして使ってください。

    Optional についても同様です。

    動くコードをいっぺんに全部回答するのは分量が多すぎるのでやりません。詰まった箇所があればまた質問してください。その際は分からない箇所以外の余計な部分を削ったコードを用意していただけると答えやすいです。
  3. @kanfutrooper

    Questioner

    @uasiさん、丁寧な解説付きの回答及び学習方法のアドバイス、本当にありがとうございます。
    学習方法のアドバイスが理解できてなくて、申し訳ございませんでした。

    Basic の解説ア通り、
    『const [basicProfile, setBasicProfile] = React.useState({ gender: null, year: null, month: null, day: null ));を Content コンポーネントの中に書いて
    <StepContent 略 BasicProps={{ basicProfile, setBasicProfile }} />
    のように渡してください。 Basic コンポーネント側では
    const Basic = ({ basicProfile, setBasicProfile }) => { 略 }』を入力したのですが、
    Basic.js の質問及び答えた回答がContent.js case3で表示されませんでした。
    エラー表示も出ないので、どこが問題なのかがわかりません。
  4. basicProfile は回答を保存する入れ物で、 setBasicProfile は入れ物に回答をセットする関数だということは理解できていますか?

    > Basic コンポーネント側では
    > const Basic = ({ basicProfile, setBasicProfile }) => { 略 }』を入力したのですが、

    この段階では Basic コンポーネントに入れ物とセッター関数を渡しただけです。入れ物に入っている回答を表示するコードや、セッター関数を使って回答を保存するコードを書かなければ回答は表示できません。

    そのコードの書き方は、上で言ったように

    https://mui.com/api/select/
    https://mui.com/api/radio-group/

    を参考にしてください。
  5. このページも参考になります。 https://ja.reactjs.org/docs/forms.html#controlled-components

    コードはだいたいこんな感じになるんじゃないかと思います。動かしていないので間違っているかもしれませんが。

    <RadioGroup 略 value={basicProfile.gender} onChange={(evt) =>
    setBasicProfile((state) => {
    return { ...state, gender: evt.target.value }
    })
    }>
  6. @kanfutrooper

    Questioner

    @uasiさん、丁寧な解説とアドバイス、ありがとうございます。
    ドキュメントは、書いてあることがclassコンポーネントで書いてあるので、
    関数コンポーネントが完璧理解をできていない僕にはハードルが高いです。
    もう一度、質問をし直します。
  7. 確かに今からクラスコンポーネントを覚えるのは大変かもしれませんね。

    最後に貼ったドキュメントの要点は、コントロールに state の値を反映させるやり方が2種類あり、それぞれ制御コントロールと非制御コントロールと呼ぶ、ということです。違いがあるということだけ記憶に留めていただければまずは大丈夫です。
  8. @kanfutrooper

    Questioner

    @uasiさん、アドバイスありがとうございました!
    性別の生年月日の質問及び答えた回答は、表示することができました!
    本当にありがとうございました!
  9. おめでとうございます!
  10. @kanfutrooper

    Questioner

    @uasiさん、丁寧な解説付きの回答、本当にありがとうございました!
    記載した相談内容の実装もできました!
    本当にありがとうございました!

Your answer might help someone💌