Wails中调用Go函数后状态更新不生效怎么办?

Designer°悦辰 阅读 49

我在React组件里用Wails调用Go的fetchData函数,返回数据后setState就是不更新界面,卡成加载状态了。

代码是这样的:


function App() {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    wails.Events.On("dataReady", (result) => {
      console.log("Got data:", result); // 这里能正常打印数据
      setData(result); // 这行执行后界面没变化
    });
    
    wails.Go.Run("GetData").catch(err => console.error(err));
  }, []);

  return <div>{data ? JSON.stringify(data) : "Loading..."}</div>;
}

尝试过把setData包在useCallback里,也试过用forceUpdate,但都不行。控制台没报错,就是状态没变。是不是Wails的事件循环有问题?或者需要手动触发重渲染?

我来解答 赞 8 收藏
二维码
手机扫码查看
2 条解答
东方智营
这个问题我之前也踩过坑,Wails 的事件回调是在 Go 的 goroutine 里触发的,而 React 的 setState 是在 JS 主线程跑的,事件触发时 React 根本不知道要重渲染,状态虽然设进去了,但组件没收到更新信号。

一般这样处理:用 React.useCallback 包一层,或者更稳妥的是把 setData 提前用 useRef 缓存,然后在事件回调里直接用 ref.current 调用,避免闭包陷阱;另外关键是得确保回调函数是在 React 的执行上下文里跑。

不过最简单直接的解法是:用 useEffect 监听一个临时状态(比如用一个 counter 或者单独的 updateTrigger),或者更干脆点——直接用 useReducer 替代 useState,因为 reducer 是同步的,不会被跨线程打断。

我实际项目里常用的是下面这种写法,可靠:

function App() {
const [data, setData] = useState(null);
const [_, forceUpdate] = useState(0);

useEffect(() => {
const unbind = wails.Events.On("dataReady", (result) => {
console.log("Got data:", result);
setData(result);
forceUpdate(x => x + 1); // 强制触发一次重渲染
});

wails.Go.Run("GetData").catch(err => console.error(err));

return () => {
unbind();
};
}, []);

return
{data ? JSON.stringify(data) : "Loading..."}
;
}


不过要提醒一句,这种强制更新是下策,如果数据结构复杂或者频繁更新,建议改成用 useReducer

function reducer(state, action) {
switch (action.type) {
case 'SET_DATA':
return { ...state, data: action.payload };
default:
return state;
}
}

function App() {
const [state, dispatch] = useReducer(reducer, { data: null });

useEffect(() => {
const unbind = wails.Events.On("dataReady", (result) => {
dispatch({ type: 'SET_DATA', payload: result });
});

wails.Go.Run("GetData").catch(err => console.error(err));

return () => unbind();
}, []);

return
{state.data ? JSON.stringify(state.data) : "Loading..."}
;
}


这样写就不用管渲染时机的问题,reducer 是同步执行的,Wails 回调里直接派发 action 就行,我试过多次都稳。
点赞 3
2026-02-27 11:07
UP主~淑怡
这个问题其实跟Wails的事件循环没啥关系,主要是React的状态更新机制和异步事件处理的问题。React的useState是异步的,但更重要的是,wails.Events.On注册的回调函数可能不在React的上下文中执行,这会导致状态更新不触发重渲染。

推荐的做法是确保回调函数在React的上下文中运行。你可以用useEffectuseRef来解决这个问题。把事件监听器的注册逻辑放到useEffect里,并通过一个ref来存储数据,然后在useEffect中监听这个ref的变化并调用setData

下面是修改后的代码:
function App() {
const [data, setData] = useState(null);
const dataRef = useRef(null);

useEffect(() => {
const handler = (result) => {
console.log("Got data:", result);
dataRef.current = result;
setData(result); // 确保在React上下文中更新状态
};

wails.Events.On("dataReady", handler);

wails.Go.Run("GetData").catch(err => console.error(err));

return () => {
wails.Events.Off("dataReady", handler); // 清理事件监听器,避免内存泄漏
};
}, []);

return <div>{data ? JSON.stringify(data) : "Loading..."}</div>;
}


这里的关键点有两个:一是通过useEffect确保事件监听器只注册一次,并且在组件卸载时清理掉;二是直接在事件回调中调用setData,不需要额外的useCallbackforceUpdate

如果还是不行,可以检查一下你的Go代码是不是没有正确触发dataReady事件,或者返回的数据格式有问题。根据Wails官方文档,事件传递的数据应该是可以被JSON序列化的,如果数据结构太复杂可能会导致问题。

最后提醒一句,调试这种跨语言框架的时候,打印日志真的很重要,别嫌麻烦。
点赞 8
2026-02-17 11:08