为什么我的FigJam白板在React中渲染时会重复加载数据?

设计师令敏 阅读 12

最近在用React集成Figma的FigJam白板组件,发现每次保存内容后都会触发两次API请求,导致数据重复加载。明明设置了依赖项,但控制台还是显示重复的日志,这是为什么呢?

我的组件逻辑大概是这样写的:


import { useFigjam } from 'figjam-react';

function Whiteboard() {
  const [boardData, setBoardData] = useState(null);
  
  useEffect(() => {
    const loadBoard = async () => {
      const data = await fetchBoard('project123');
      setBoardData(data);
      console.log('数据加载完成:', data.id); // 这里重复打印两次
    };
    loadBoard();
    
    return () => {
      console.log('清理钩子执行');
    };
  }, []); // 确定依赖数组是空的

  return <FigjamBoard data={boardData} onSave={setBoardData} />;
}

问题出现在保存内容时,白板内容会短暂闪烁重载,同时网络面板显示每次保存都发起了两次POST请求。我检查过API端点没有问题,也尝试过在onSave回调里加防抖,但现象依旧。有没有可能是Figjam组件和React状态更新机制的兼容性问题?

我来解答 赞 4 收藏
二维码
手机扫码查看
2 条解答
ლ景叶
ლ景叶 Lv1
从你描述的现象来看,问题大概率出在React的严格模式和FigJam组件的状态管理上。如果你用的是React 18,默认情况下开发环境会启用严格模式,它会让组件挂载、卸载再重新挂载一次,这会导致useEffect里的逻辑被执行两次,这就是为什么你会看到重复的日志打印。

解决这个问题的办法有几个,但要特别注意安全性和代码的可维护性。首先建议你在onSave回调里做一层状态防抖和请求节流,防止短时间内多次触发保存操作。可以用lodash的debounce方法,或者自己实现一个简单的防抖逻辑。

另外,确保你的fetchBoard函数对输入参数做了校验和清理,防止注入攻击。比如这样写:

const fetchBoard = async (id) => {
if (typeof id !== 'string' || !/^[a-zA-Z0-9_-]+$/.test(id)) {
throw new Error('Invalid board ID');
}
const response = await fetch(/api/boards/${encodeURIComponent(id)});
return response.json();
};


关于重复渲染的问题,我建议你检查一下FigjamBoard组件的实现。如果它是不受控组件,可能会在父组件状态更新时重置自己的状态。你可以尝试这样改写:

function Whiteboard() {
const [boardData, setBoardData] = useState(null);
const isMounted = useRef(false);

useEffect(() => {
if (isMounted.current) return;
isMounted.current = true;

const loadBoard = async () => {
try {
const data = await fetchBoard('project123');
setBoardData(data);
} catch (error) {
console.error('加载失败:', error.message);
}
};
loadBoard();

return () => {
console.log('清理钩子执行');
};
}, []);

const handleSave = useCallback((newData) => {
setBoardData(prev => ({ ...prev, ...newData }));
}, []);

return <FigjamBoard data={boardData} onSave={handleSave} />;
}


这里用了useRef来标记组件是否已经挂载过,避免严格模式下的重复执行。同时把onSave改成useCallback包裹,保证引用稳定性。

最后提醒一下,生产环境下React不会重复执行useEffect,所以这个问题只会在开发环境出现。不过为了代码的健壮性,还是建议按照上面的方式处理一下。调试的时候可以暂时关闭React严格模式来确认问题,但不要依赖这种方式来解决问题。
点赞 1
2026-02-17 15:03
UI雨诺
UI雨诺 Lv1
从你的描述来看,问题大概率出在React的副作用和FigJam组件的交互上。我先说结论:useEffect的依赖数组虽然是空的,但onSave回调触发的状态更新会导致组件重新渲染,而FigJam组件可能在内部也有自己的状态管理逻辑,这就会引发重复加载的问题。

我们来一步步分析。首先,useEffect只会在组件挂载和卸载时执行一次,这点你已经确认了。但每次保存内容时,onSave会调用setBoardData更新状态,这会让React重新渲染组件。如果FigjamBoard组件内部对data属性的变化有监听逻辑,它可能会触发额外的API请求或者状态同步操作,导致重复加载。

其次,fetchBoard函数本身可能是异步的,如果它的调用没有防抖或节流机制,在快速保存时可能会堆积多个请求。虽然你提到加了防抖,但如果防抖的范围只覆盖了onSave回调,而没有处理FigjamBoard内部的行为,那问题依然存在。

解决这个问题可以从以下几个方面入手:

第一,确保fetchBoard的调用是幂等的,并且在保存操作完成前禁用重复调用。可以在组件中维护一个标志位,比如isLoading,用来防止并发请求。示例代码如下:


import { useFigjam } from 'figjam-react';

function Whiteboard() {
const [boardData, setBoardData] = useState(null);
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
const loadBoard = async () => {
if (isLoading) return; // 防止重复加载
setIsLoading(true);
try {
const data = await fetchBoard('project123');
setBoardData(data);
console.log('数据加载完成:', data.id);
} finally {
setIsLoading(false);
}
};
loadBoard();

return () => {
console.log('清理钩子执行');
};
}, []); // 确保依赖数组为空

const handleSave = useCallback((newData) => {
if (isLoading) return; // 同样防止重复保存
setBoardData(newData);
}, [isLoading]);

return <FigjamBoard data={boardData} onSave={handleSave} />;
}


第二,检查FigjamBoard组件的实现。如果它是第三方库提供的,看看文档里有没有提到如何避免重复加载的问题。如果没有明确说明,可以尝试在父组件中控制它的key属性,强制让它在特定条件下重新挂载。比如:


return <FigjamBoard key={boardData?.id} data={boardData} onSave={handleSave} />;


这样做的好处是,当boardData.id变化时,FigjamBoard会被完全销毁并重新创建,避免内部状态残留导致的重复行为。

最后,如果你发现问题依然存在,建议在服务端加一些日志,看看两次POST请求的来源是否一致。有时候前端的问题可能和服务端的响应有关,比如服务端返回了不一致的数据,导致前端误以为需要重新加载。

总之,这类问题通常是前端状态管理和组件生命周期的交互导致的,排查时要重点关注状态更新的触发点和第三方组件的行为。希望这些建议能帮你解决问题。
点赞 1
2026-02-16 21:09