简体   繁体   English

如何在 React 中同步组件共享状态钩子

[英]How to sync components sharing state hook in React

I'm trying to make 2 components in sync with their parent component state using React hooks.我正在尝试使用 React 钩子使 2 个组件与其父组件状态同步。 I'm using horizontal and vertical stepper as 2 separate components from material UI, and their parent class has the content of the stepper and the state in which they both should share.我使用水平垂直步进器作为 2 个独立于材质 UI 的组件,它们的父类具有步进器的内容和它们都应该共享的状态。 The reason I'm using the horizontal and vertical stepper is to make the UI responsive as possible.我使用水平垂直步进器的原因是使 UI 尽可能响应。

The problem I'm encountering is that when the activeStep is incremented by one of the components ie horizontal stepper, upon my understanding of the component mount life cycle.我遇到的问题是,当activeStep由一个组件(即水平步进器)递增时,根据我对组件安装生命周期的理解。 the render method is called and the Activestep is incremented and reflected in the dom.调用渲染方法,并且 Activestep 递增并反映在 dom 中。 but it is only reflected in the horizontal stepper.但它仅反映在水平步进器中。 the change only propagates in the horizontal stepper component.更改仅在水平步进器组件中传播。 when navigating the vertical stepper component, it returns the initial state of the hook which was originally set to 0.当导航垂直步进组件时,它返回最初设置为 0 的钩子的初始状态。

I'm trying to make Horizontal and Vertical stepper in sync with the activeStep in the stepperContent , and any change in the state should propagate in both components.我正在尝试使Horizo​​ntalVertical 步进器stepperContent 中activeStep同步,并且状态的任何更改都应在两个组件中传播。

My Question is我的问题是

How do I make them in sync with the activeState in stepperContent stateful functional component?如何使它们与stepperContent有状态功能组件中的activeState同步?

steppertContent.JSX 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水平窗体步进器.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 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;

Another possible solution, is to use Context API.另一种可能的解决方案是使用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>
  );
};

So instead of using StepperContent , you can now use useStepperContent hook.因此,而不是使用的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

Might be overkill, but it's there.可能有点矫枉过正,但它就在那里。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM