export function useShape (){
const [state, setState] = useState({
shape: 'square',
size: {
width: 100,
height: 100
}
})
// Change shape name while size update
useEffect(()=>{
const {size: {width, height}} = state
setState({
...state,
shape: width===height ? 'square' : 'rect'
})
}, [state, state.size])
}
更新大小后,副作用将根据宽度,高度更改大小名称。
目标是使状态保持一致,因此无论大小如何变化,我将始终获得正确的形状。
useEffect函数陷入循环,如果我删除“状态”依赖关系会很好,但是intelliSense请求“状态”依赖关系,那么解决方案是什么?
答案 0 :(得分:2)
useEffect函数陷入循环...
那是因为您每次都为state
创建一个新对象,并且state
被列为依赖项。
使用钩子时,通常最好使用较小的部分,而不是构建多部分状态对象。在这种情况下,我将使用三个:
const [shape, setShape] = useState("square");
const [width, setWidth] = useState(100);
const [height, setHeight] = useState(100);
useEffect(() => {
setShape(width === height ? "square" : "rect");
}, [width, height]);
现在,您所设置的(shape
并不是效果钩子的依赖项,因此不会无休止地触发。
const {useState, useEffect} = React;
function Example() {
const [shape, setShape] = useState("square");
const [width, setWidth] = useState(100);
const [height, setHeight] = useState(100);
useEffect(() => {
setShape(width === height ? "square" : "rect");
}, [width, height]);
function onWidthInput({target: {value}}) {
setWidth(+value);
}
function onHeightInput({target: {value}}) {
setHeight(+value);
}
return <div>
<div>
Width: <input type="number" value={width} onInput={onWidthInput} />
</div>
<div>
Height: <input type="number" value={height} onInput={onHeightInput} />
</div>
<div>
Shape: {shape}
</div>
</div>;
}
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
不过,您可以根据需要使用现有的状态对象来做到这一点,
useEffect(() => {
setState(current => {
const {size: {width, height}} = current;
return {
...current,
shape: width === height ? "square" : "rect"
};
});
}, [state.size, state.size.width, state.size.height]);
const {useState, useEffect} = React;
function Example() {
const [state, setState] = useState({
shape: 'square',
size: {
width: 100,
height: 100
}
});
useEffect(() => {
setState(current => {
const {size: {width, height}} = current;
return {
...current,
shape: width === height ? "square" : "rect"
};
});
}, [state.size, state.size.width, state.size.height]);
function onSizePropInput({target: {name, value}}) {
setState(current => {
return {
...current,
size: {
...current.size,
[name]: +value
}
};
});
}
const {shape, size: {width, height}} = state;
return <div>
<div>
Width: <input type="number" name="width" value={width} onInput={onSizePropInput} />
</div>
<div>
Height: <input type="number" name="height" value={height} onInput={onSizePropInput} />
</div>
<div>
Shape: {shape}
</div>
</div>;
}
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
请注意其中使用了setState
回调形式。您想要状态的 then-current 版本,而不是第一次创建效果回调时的状态,因为用于更新的部分内容不是依赖项(因此可能变得陈旧)。
答案 1 :(得分:1)
您可以查看此sandbox link以获得解决方案
无限循环是由于在useEffect中修改状态本身时将“状态”作为依赖项。
解决方案是通过使视图变量与受控变量分开来解除状态的耦合。
这是定义useShape挂钩的方法。
function useShape() {
const [shape, setShape] = useState("square");
const [dimension, setDimension] = useState({
width: 100,
height: 100
});
// Change shape name while size update
useEffect(() => {
const { height, width } = dimension;
setShape(height === width ? "square" : "react");
}, [dimension, dimension.height, dimension.width]);
return [shape, setDimension];
}
通过这种方式,您可以公开Dimension setter,并将变量视为独立的部件。