Как синхронизировать компоненты, разделяющие перехват состояния в React

#javascript #reactjs #material-ui #react-hooks

#javascript #reactjs #материал-пользовательский интерфейс #реагирующие перехваты

Вопрос:

Я пытаюсь синхронизировать 2 компонента с состоянием их родительского компонента, используя перехваты React. Я использую горизонтальный и вертикальный шаговые устройства как 2 отдельных компонента из пользовательского интерфейса material, а их родительский класс содержит содержимое шагового устройства и состояние, в котором они оба должны совместно использоваться. Причина, по которой я использую горизонтальный и вертикальный шаговый переход, заключается в том, чтобы сделать пользовательский интерфейс максимально адаптивным.

Проблема, с которой я сталкиваюсь, заключается в том, что, когда activeStep увеличивается на один из компонентов, т. Е. на горизонтальный шаговый, насколько я понимаю, жизненный цикл монтирования компонента. вызывается метод визуализации, и Activestep увеличивается и отражается в dom. но это отражается только в горизонтальном шаговом механизме. изменение распространяется только в горизонтальном шаговом компоненте. при навигации по компоненту вертикального шагового перехода он возвращает начальное состояние перехвата, которое изначально было установлено в 0.

Я пытаюсь синхронизировать горизонтальный и вертикальный шаговые операции с activeStep в stepperContent, и любое изменение состояния должно распространяться в обоих компонентах.

Мой вопрос

Как мне синхронизировать их с функциональным компонентом с отслеживанием состояния activeState в stepperContent?

steppertContent.JSX

 import { makeStyles } from "@material-ui/core/styles";
import { useState, useEffect } from "react";

export const useVerticalStyles = makeStyles((theme) => ({
  root: {
    width: "100%",
  },
  button: {
    marginTop: theme.spacing(1),
    marginRight: theme.spacing(1),
  },
  actionsContainer: {
    marginBottom: theme.spacing(2),
  },
  resetContainer: {
    padding: theme.spacing(3),
  },
}));

export const useHorizontalStyles = makeStyles((theme) => ({
  root: {
    width: "100%",
  },
  backButton: {
    marginRight: theme.spacing(1),
  },
  instructions: {
    marginTop: theme.spacing(1),
    marginBottom: theme.spacing(1),
  },
}));

export const StepperContent = () => {
  const [activeStep, setActiveStep] = useState(0);

  useEffect(() => {
    console.log(activeStep);
  }, [activeStep]);
  // console.log(activeStep);

  const handleNext = () => {
    setActiveStep((prevActiveStep) => prevActiveStep   1);
  };

  const handleBack = () => {
    setActiveStep((prevActiveStep) => prevActiveStep - 1);
  };

  const handleReset = () => {
    setActiveStep(0);
  };
  return { activeStep, handleNext, handleBack, handleReset };
};

export const getSteps = () => {
  return [
    "ACCOUNT SETUP",
    "PERSONAL INFORMATION",
    "CONTACT INFORMATION",
    "FAMILY INFORMATION",
    "SCHOOL INFORMATION",
    "ADMISSION INFORMATION",
    "SUBMIT INFORMATION",
  ];
};

export const getStepContent = (stepIndex) => {
  switch (stepIndex) {
    case 0:
      return "CREATE YOUR ACCOUNT";
    case 1:
      return "What is an ad group anyways?";
    case 2:
      return "This is the bit I really care about!";
    default:
      return "Unknown stepIndex";
  }
};

  

horizontalFormStepper.JSX

 import React from "react";
import {
  Stepper,
  Step,
  StepLabel,
  Button,
  Typography,
} from "@material-ui/core/";
import {
  getStepContent,
  getSteps,
  useHorizontalStyles,
  StepperContent,
} from "./common/stepperContent";

const HorizontalFormStepper = () => {
  const classes = useHorizontalStyles();
  const { activeStep, handleReset, handleBack, handleNext } = StepperContent();
  const steps = getSteps();

  return (
    <div className={classes.root}>
      <Stepper activeStep={activeStep} alternativeLabel>
        {steps.map((label) => (
          <Step key={label}>
            <StepLabel>{label}</StepLabel>
          </Step>
        ))}
      </Stepper>
      <div>
        {activeStep === steps.length ? (
          <div>
            <Typography className={classes.instructions}>
              All steps completed
            </Typography>
            <Button onClick={handleReset}>Reset</Button>
          </div>
        ) : (
          <div>
            <Typography className={classes.instructions}>
              {getStepContent(activeStep)}
            </Typography>
            <div>
              <Button
                disabled={activeStep === 0}
                onClick={handleBack}
                className={classes.backButton}
              >
                Back
              </Button>
              <Button variant="contained" color="primary" onClick={handleNext}>
                {activeStep === steps.length - 1 ? "Finish" : "Next"}
                {/* {console.log(steps.length - 1)} */}
              </Button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

export default HorizontalFormStepper;
  

verticalFormStepper.JSX

 import React from "react";
import {
  Stepper,
  Step,
  StepLabel,
  StepContent,
  Button,
  Paper,
  Typography,
  Grid,
  Container,
} from "@material-ui/core/";
import {
  getStepContent,
  getSteps,
  useVerticalStyles,
  StepperContent,
} from "./common/stepperContent";

const VerticalFormStepper = () => {
  const classes = useVerticalStyles();
  const steps = getSteps();
  const { activeStep, handleBack, handleNext, handleReset } = StepperContent();
  return (
    <Container fixed maxWidth="sm">
      <Grid>
        <Paper variant="outlined" elevation={2}>
          <div className={classes.root}>
            <Stepper activeStep={activeStep} orientation="vertical">
              {steps.map((label, index) => (
                <Step key={label}>
                  <StepLabel>{label}</StepLabel>
                  <StepContent>
                    <Typography>{getStepContent(index)}</Typography>
                    <div className={classes.actionsContainer}>
                      <div>
                        <Button
                          disabled={activeStep === 0}
                          onClick={handleBack}
                          className={classes.button}
                        >
                          Back
                        </Button>
                        <Button
                          variant="contained"
                          color="primary"
                          onClick={handleNext}
                          className={classes.button}
                        >
                          {activeStep === steps.length - 1 ? "Finish" : "Next"}
                        </Button>
                      </div>
                    </div>
                  </StepContent>
                </Step>
              ))}
            </Stepper>
            {activeStep === steps.length amp;amp; (
              <Paper square elevation={0} className={classes.resetContainer}>
                <Typography>
                  All steps completed - youamp;apos;re finished
                </Typography>
                <Button onClick={handleReset} className={classes.button}>
                  Reset
                </Button>
              </Paper>
            )}
          </div>
        </Paper>
      </Grid>
    </Container>
  );
};

export default VerticalFormStepper;
  

Комментарии:

1. Похоже, вы можете поместить это в codesandbox, вы можете это сделать?

2. codesandbox.io/s/young-feather-70×21?file=/src/App.js

3. Спасибо! Итак, проблема, которую мы хотим исправить здесь, заключается в том, что мы не хотим терять прогресс пользователя при переключении на horizontal и vertical stepper — или наоборот.

4. Проблема в том, что вы вызываете StepperContent в обоих компонентах, вместо этого вызывайте его только один раз в родительском компоненте и передавайте значение результата в качестве реквизита компоненту, вот рабочая версия codesandbox.io/s/fancy-dream-5x4ko

5. Другими словами, вызывая StepperContent в каждом компоненте, вы создаете два состояния, каждый компонент имеет свое собственное состояние вместо того, чтобы они разделяли одно и то же состояние.

Ответ №1:

Другим возможным решением является использование Context API.

 // StepperContent.jsx
...

export const StepperContentContext = createContext();
export const useStepperContent = () => useContext(StepperContentContext);

export const StepperContentProvider = ({ children }) => {
  ...

  const value = { activeStep, handleNext, handleBack, handleReset };

  return (
    <StepperContentContext.Provider value={value}>
      {children}
    </StepperContentContext.Provider>
  );
};
  

Таким образом, вместо использования StepperContent теперь вы можете использовать useStepperContent перехват.

 // HorizontalFormStepper.jsx
...
import {
  getStepContent,
  getSteps,
  useHorizontalStyles,
  useStepperContent
} from "./common/StepperContent";

const HorizontalFormStepper = () => {
  const classes = useHorizontalStyles();
  const {
    activeStep,
    handleReset,
    handleBack,
    handleNext
  } = useStepperContent();
  ...
  

Редактировать sleepy-bell-2pgq2

Может быть излишним, но это есть.