示例代码
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函数进入了一个循环,如果我删除"state"依赖关系,这将是好的,但intelliSense请求"state"依存关系,那么解决方案是什么?
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
的回调形式在其中的使用。您想要的是状态的当前版本,而不是第一次创建效果回调时的状态,因为您用于更新的部分内容不是依赖项(因此可能已经过时(。
您可以查看此解决方案的沙箱链接
无限循环是由于放置";状态";作为依赖项,同时在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,并将变量视为独立的Pieces。