我正在尝试构建一个简单的Material UI Stepper,以允许用户在 Next 和 Back 中以及在该步骤中单击,但它会触发reducer两次。
我已经读过somewhere的解决方案是useCallback
或useMemo
钩子,它们避免了多次实例化函数,仅在函数更改时才返回该函数或结果。>
我的问题是,仍然是一个清晰的示例,我不确定如何将其应用于我的代码。我将使用简单的状态管理,该方法效果很好。但是我想学习这个...
这是我的App
函数:
function App() {
const [completed, setCompleted] = React.useState({});
const [activeStep, dispatchActiveStep] = React.useReducer((step, action) => {
let completedSteps = completed;
let active = step;
switch (action.type) {
case "next":
if (step < steps.length) {
completedSteps[activeStep] = true;
active = step + 1;
}
break;
case "previous":
if (step > 0) {
delete completed[activeStep];
active = step - 1;
}
break;
case "set":
if (!(action.step in Object.keys(completed))) {
console.error("step not completed");
return step;
}
if (action.step === 0) {
completedSteps = {};
active = 0;
} else if (action.step === steps.length - 1) {
completedSteps = {};
for (let i = 0; i <= action.step; i++) {
completedSteps[i] = true;
}
active = action.step;
}
break;
default:
console.error("action not available");
}
console.log("test");
setCompleted(completedSteps);
return active;
}, 0);
return (
<Paper>
<Stepper activeStep={activeStep}>
{steps.map((step, i) => (
<Step key={i}>
<StepButton
key={i}
completed={completed[i]}
onClick={() => dispatchActiveStep({ type: "set", step: i })}
>
<Typography>{step.label}</Typography>
</StepButton>
</Step>
))}
</Stepper>
{steps.map((step, i) => {
if (activeStep === i) {
return (
<div key={i} style={styles.content}>
{step.component}
</div>
);
}
})}
<div style={styles.buttons}>
<Button
color="primary"
variant="contained"
onClick={() => dispatchActiveStep({ type: "previous" })}
disabled={activeStep === 0}
>
Previous
</Button>
<Button
color="secondary"
variant="contained"
style={{ marginLeft: "10px" }}
onClick={() => dispatchActiveStep({ type: "next" })}
disabled={activeStep === steps.length - 1}
>
Next
</Button>
</div>
</Paper>
);
}
我已经尝试了这段代码,但是由于dispatchActiveStep()
被调用时仍然会重新渲染,因此仍然无法正常工作
function App() {
const [completed, setCompleted] = React.useState({});
const [activeStep, setActiveStep] = React.useState(0);
const handleBack = () => {
let completedSteps = completed;
if (activeStep === steps.length - 1) {
delete completedSteps[activeStep - 1];
} else {
delete completedSteps[activeStep];
}
setCompleted(completedSteps);
setActiveStep(activeStep - 1);
};
const handleNext = () => {
let completedSteps = completed;
completedSteps[activeStep] = true;
setCompleted(completedSteps);
setActiveStep(activeStep + 1);
};
const handleClick = step => {
let completedSteps = completed;
if (!(step in Object.keys(completedSteps))) {
console.error("step not completed");
return;
}
completedSteps = {};
for (let i = 0; i < step; i++) {
completedSteps[i] = true;
}
setActiveStep(step);
setCompleted(completedSteps);
};
return (
<Paper>
<Stepper activeStep={activeStep}>
{steps.map((step, i) => (
<Step key={i}>
<StepButton
key={i}
completed={completed[i]}
onClick={() => {
handleClick(i);
}}
>
<Typography>{step.label}</Typography>
</StepButton>
</Step>
))}
</Stepper>
{steps.map((step, i) => {
if (activeStep === i) {
return (
<div key={i} style={styles.content}>
{step.component}
</div>
);
}
})}
<div style={styles.buttons}>
<Button
color="primary"
variant="contained"
onClick={handleBack}
disabled={activeStep === 0}
>
Previous
</Button>
<Button
color="secondary"
variant="contained"
style={{ marginLeft: "10px" }}
onClick={handleNext}
disabled={activeStep === steps.length - 1}
>
Next
</Button>
</div>
</Paper>
);
}
答案 0 :(得分:0)
以下是使用useReducer的解决方案:CodeSandbox
我不确定您的组件被重新渲染两次的确切原因,但是useState
和useReducer
的混合以及在reducer中的副作用对我来说似乎是错误的,所以我决定仅使用useReducer
进行重写。可能是由于dispatchActiveStep
和setCompleted
渲染了两次,因为它们都触发了重新渲染。
编辑:实际上,重新渲染的原因是您在组件内部定义了一个reducer,并且每次都会重新创建它:useReducer Action dispatched twice