【问题标题】:How to sync components sharing state hook in React如何在 React 中同步组件共享状态挂钩
【发布时间】:2020-12-30 14:20:56
【问题描述】:

我正在尝试使用 React 钩子使 2 个组件与它们的父组件状态同步。 我使用 horizo​​ntalvertical stepper 作为 Material UI 中的 2 个独立组件,它们的父类具有 stepper 的内容和它们的状态 应该分享。 我使用 horizo​​ntalvertical stepper 的原因是为了让 UI 尽可能响应。

我遇到的问题是,当activeStep 增加一个组件(即水平步进器)时,根据我对组件安装生命周期的理解。调用 render 方法,Activestep 递增并反映在 dom 中。但它只体现在水平步进器上。更改仅在水平步进器组件中传播。在导航 vertical stepper 组件时,它会返回钩子的初始状态,该状态最初设置为 0。

我正在尝试使 Horizo​​ntalVertical stepperstepperContent 中的 activeStep 同步,并且状态应该在两个组件中传播。

我的问题是

如何使它们与 stepperContent 有状态功能组件中的activeState 同步?

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";
  }
};

horizo​​ntalFormStepper.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 && (
              <Paper square elevation={0} className={classes.resetContainer}>
                <Typography>
                  All steps completed - you&apos;re finished
                </Typography>
                <Button onClick={handleReset} className={classes.button}>
                  Reset
                </Button>
              </Paper>
            )}
          </div>
        </Paper>
      </Grid>
    </Container>
  );
};

export default VerticalFormStepper;

【问题讨论】:

  • 看起来你可以把这个放在codesandbox上,你能做到吗?
  • 谢谢!所以我们要在这里解决的问题是,我们不想在切换到horizontalvertical stepper 时丢失用户的进度——反之亦然。
  • 问题是你在两个组件中都调用StepperContent,而不是那样,在父组件中只调用一次并将结果值作为道具传递给组件,这是一个工作版本codesandbox.io/s/fancy-dream-5x4ko
  • 换句话说,通过在每个组件中调用StepperContent,您将创建两个状态,每个组件都有自己的状态,而不是共享相同的状态。

标签: javascript reactjs material-ui react-hooks


【解决方案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>
  );
};

因此,您现在可以使用useStepperContent 挂钩,而不是使用StepperContent

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

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

可能有点矫枉过正,但它就在那里。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-31
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 2020-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多