React 状态总是在来自 fabricjs 的回调中返回先前(或初始)状态
React state always returning previous (or initial) state inside callback from fabricjs
下面的代码是我的最小问题重现组件。它初始化结构 canvas,并处理“模式”状态。模式状态决定 canvas 是否可以编辑,一个简单的按钮控制该状态。
问题是即使 mode,setMode
工作正常(意思是 - 组件分析器在单击按钮后显示正确状态,按钮内的文本也显示正确状态),从 mode
钩子内部返回的状态fabric 事件回调,仍然是 returns 初始状态。
我想问题出在作为回调传递给 fabric 事件的函数。似乎回调以某种方式被“缓存”,因此在该回调内部,所有状态都有初始值,或者在传递该回调之前处于状态的值。
如何让它正常工作?我想访问 fabric 回调中正确的当前状态。
const [canvas, setCanvas] = useState<fabric.Canvas | null>(null);
const [mode, setMode] = useState("freerun");
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const modes = ["freerun", "edit"];
React.useEffect(() => {
const canvas = new fabric.Canvas(canvasRef.current, {
height: 800,
width: 800,
backgroundColor: 'yellow'
});
canvas.on('mouse:down', function (this: typeof canvas, opt: fabric.IEvent) {
const evt = opt.e as any;
console.log("currentMode", mode) // Not UPDATING - even though components profiler shows that "mode" state is now "edit", it still returns initial state - "freerun".
if (mode === "edit") {
console.log("edit mode, allow to scroll, etc...");
}
});
setCanvas(canvas);
return () => canvas.dispose();
}, [canvasRef])
const setNextMode = () => {
const index = modes.findIndex(elem => elem === mode);
const nextIndex = index + 1;
if (nextIndex >= modes.length) {
setMode(modes[0])
} else {
setMode(modes[nextIndex]);
}
}
return (
<>
<div>
<button onClick={setNextMode}>Current mode: { mode }</button>
</div>
{`Current width: ${width}`}
<div id="fabric-canvas-wrapper">
<canvas ref={canvasRef} />
</div>
</>
)
问题是 mode
被读取,它的值在回调创建期间保存在回调中,并且从那里不再更新。
为了解决这个问题,您必须在 useEffect
依赖项上添加 mode
。这样,每次 mode
更改时,React 都会 运行 再次 useEffect
并且回调将接收更新的(和正确的)值。
没错!现在成功了,谢谢 Marco。
现在 不是 运行 setCanvas
在每次模式更改时,我最终创建了另一个 useEffect
挂钩来保持附加 canvas 事件 仅 。最终代码看起来类似于:
const [canvas, setCanvas] = useState<fabric.Canvas | null>(null);
const [mode, setMode] = useState("freerun");
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const modes = ["freerun", "edit"];
React.useEffect(() => {
if (!canvas) {
return;
}
// hook for attaching canvas events
fabric.Image.fromURL(gd, (img) => {
if (canvas) {
canvas.add(img)
disableImageEdition(img);
}
});
canvas.on('mouse:down', function (this: typeof canvas, opt: fabric.IEvent) {
const evt = opt.e as any;
console.log("currentMode", mode) // works correctly now
if (mode === "edit") {
console.log("edit mode, allow to scroll, etc...");
}
});
}, [canvas, mode])
React.useEffect(() => {
const canvas = new fabric.Canvas(canvasRef.current, {
height: 800,
width: 800,
backgroundColor: 'yellow'
});
setCanvas(canvas);
return () => canvas.dispose();
}, [canvasRef])
const setNextMode = () => {
const index = modes.findIndex(elem => elem === mode);
const nextIndex = index + 1;
if (nextIndex >= modes.length) {
setMode(modes[0])
} else {
setMode(modes[nextIndex]);
}
}
return (
<>
<div>
<button onClick={setNextMode}>Current mode: { mode }</button>
</div>
{`Current width: ${width}`}
<div id="fabric-canvas-wrapper">
<canvas ref={canvasRef} />
</div>
</>
)
我也想知道是否有更多方法可以解决这个问题 - 是否可以使用 useCallback
钩子来解决这个问题?
下面的代码是我的最小问题重现组件。它初始化结构 canvas,并处理“模式”状态。模式状态决定 canvas 是否可以编辑,一个简单的按钮控制该状态。
问题是即使 mode,setMode
工作正常(意思是 - 组件分析器在单击按钮后显示正确状态,按钮内的文本也显示正确状态),从 mode
钩子内部返回的状态fabric 事件回调,仍然是 returns 初始状态。
我想问题出在作为回调传递给 fabric 事件的函数。似乎回调以某种方式被“缓存”,因此在该回调内部,所有状态都有初始值,或者在传递该回调之前处于状态的值。
如何让它正常工作?我想访问 fabric 回调中正确的当前状态。
const [canvas, setCanvas] = useState<fabric.Canvas | null>(null);
const [mode, setMode] = useState("freerun");
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const modes = ["freerun", "edit"];
React.useEffect(() => {
const canvas = new fabric.Canvas(canvasRef.current, {
height: 800,
width: 800,
backgroundColor: 'yellow'
});
canvas.on('mouse:down', function (this: typeof canvas, opt: fabric.IEvent) {
const evt = opt.e as any;
console.log("currentMode", mode) // Not UPDATING - even though components profiler shows that "mode" state is now "edit", it still returns initial state - "freerun".
if (mode === "edit") {
console.log("edit mode, allow to scroll, etc...");
}
});
setCanvas(canvas);
return () => canvas.dispose();
}, [canvasRef])
const setNextMode = () => {
const index = modes.findIndex(elem => elem === mode);
const nextIndex = index + 1;
if (nextIndex >= modes.length) {
setMode(modes[0])
} else {
setMode(modes[nextIndex]);
}
}
return (
<>
<div>
<button onClick={setNextMode}>Current mode: { mode }</button>
</div>
{`Current width: ${width}`}
<div id="fabric-canvas-wrapper">
<canvas ref={canvasRef} />
</div>
</>
)
问题是 mode
被读取,它的值在回调创建期间保存在回调中,并且从那里不再更新。
为了解决这个问题,您必须在 useEffect
依赖项上添加 mode
。这样,每次 mode
更改时,React 都会 运行 再次 useEffect
并且回调将接收更新的(和正确的)值。
没错!现在成功了,谢谢 Marco。
现在 不是 运行 setCanvas
在每次模式更改时,我最终创建了另一个 useEffect
挂钩来保持附加 canvas 事件 仅 。最终代码看起来类似于:
const [canvas, setCanvas] = useState<fabric.Canvas | null>(null);
const [mode, setMode] = useState("freerun");
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const modes = ["freerun", "edit"];
React.useEffect(() => {
if (!canvas) {
return;
}
// hook for attaching canvas events
fabric.Image.fromURL(gd, (img) => {
if (canvas) {
canvas.add(img)
disableImageEdition(img);
}
});
canvas.on('mouse:down', function (this: typeof canvas, opt: fabric.IEvent) {
const evt = opt.e as any;
console.log("currentMode", mode) // works correctly now
if (mode === "edit") {
console.log("edit mode, allow to scroll, etc...");
}
});
}, [canvas, mode])
React.useEffect(() => {
const canvas = new fabric.Canvas(canvasRef.current, {
height: 800,
width: 800,
backgroundColor: 'yellow'
});
setCanvas(canvas);
return () => canvas.dispose();
}, [canvasRef])
const setNextMode = () => {
const index = modes.findIndex(elem => elem === mode);
const nextIndex = index + 1;
if (nextIndex >= modes.length) {
setMode(modes[0])
} else {
setMode(modes[nextIndex]);
}
}
return (
<>
<div>
<button onClick={setNextMode}>Current mode: { mode }</button>
</div>
{`Current width: ${width}`}
<div id="fabric-canvas-wrapper">
<canvas ref={canvasRef} />
</div>
</>
)
我也想知道是否有更多方法可以解决这个问题 - 是否可以使用 useCallback
钩子来解决这个问题?