【发布时间】:2019-09-29 18:04:08
【问题描述】:
我正在尝试构建一个简单的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>
);
}
【问题讨论】:
-
为什么需要减速器?你完全可以用第二个 useState 和一些函数处理程序来做到这一点
-
@mikheevm,我以前使用过 state,但是,使用 reducer 来处理这样的事情不是一个好习惯吗?
-
对于两个状态变量?我不会这么说的。我希望
useReducer可以在具有一些复杂逻辑或大量变量的组件中使用。在您的情况下,您只需要一个已完成步骤的数组和当前步骤的整数。 -
好了,我回去用简单的状态管理。我仍然想学习如何使用
useCallback和useReducer钩子。
标签: javascript reactjs react-hooks memoization