使用反应挂钩在 Office Fabric CommandBar onClick 事件中调用异步方法?

Call async method in Office Fabric CommandBar onClick event using react hooks?

我在打字稿中有一个反应钩子样式的组件。我正在使用 office uifabric 作为 ui 框架。我想让以下模式以 "best practice" 方式工作:

  1. 我有一个带有 onClick 事件的组件(在我的例子中是 CommandBar
  2. 用户点击操作
  3. 我对后端 api 进行了一个 异步调用(我认为异步部分给我带来了麻烦,不幸的是,这是一个请求 ui评论)。
  4. 异步调用完成或失败时。我想显示一个 MessageBar 的信息。
  5. 总而言之,使用尽可能少的代码,react hooks 似乎有能力与 TypeScript 一起生成漂亮且令人信服的代码。但我对这两件事都是新手,所以我可能会在这里陷入困境......

创建一个非异步的虚拟获取方法会导致我的示例代码按预期工作。但是当我进行异步调用时,有些东西丢失了,我没有得到 MessageBar 的重新渲染和可见性。 api 调用已完成!

const UserProfile = () => {
  const [apiStatusCode, setApiResponseCode] = useState(0);
  const [showMessageBar, setShowMessageBar] = useState(false);

  const startAsync = () => {
    // With await here I get a compiler
    // error. Without await, the api call is made but 
    // the messagebar is never rendered. If the call is not async, things
    // work fine.
    myAsyncMethod();
  };
  const toggleMessageBar = (): (() => void) => (): void => setShowMessageBar(!showMessageBar);

  const myAsyncMethod = async () => {
    try {
      const responseData = (await get('some/data'))!.status;
      setApiResponseCode(responseData);
      toggleMessageBar();
    } catch (error) {
      console.error(error);
      setApiResponseCode(-1);
      toggleMessageBar();
    }
  };

  const getItems = () => {
    return [
      {
        key: 'button1',
        name: 'Do something',
        cacheKey: 'button1', //
        onClick: () => startAsync()
      }
    ];
  };

  return (
    <Stack>

      <Stack.Item>
        <CommandBar
          items={getItems()}          
        />
      </Stack.Item>

      {showMessageBar &&
      <MessageBar messageBarType={MessageBarType.success} onDismiss={toggleMessageBar()} dismissButtonAriaLabel="Close">
        Successfully retrieved info with status code: {apiStatusCode}
      </MessageBar>
      }

      // Other ui parts go here        
    </Stack>
  )
};


export default UserProfile;

将异步方法分配给 onClick 事件会产生编译器错误: Type 'Promise<void>' is not assignable to type 'void'. TS2322

不等待异步调用导致在调用完成后不重新渲染。

这种事情的一个常见模式是在你正在做的时候在回调中更新你的状态,然后用 useEffect:

响应新数据
useEffect(()=>{
    setShowMessageBar(!showMessageBar)
},[apiStatusCode, showMessageBar])

const myAsyncMethod = async () => {
    try {
      const responseData = (await get('some/data'))!.status;
      setApiResponseCode(responseData);
      // toggleMessageBar(); <-- delete me
    } catch (error) {
      console.error(error);
      setApiResponseCode(-1);
      // toggleMessageBar(); <-- delete me
    }
  };

终于找到答案了,关键是使用reacts useEffect机制。这个答案让我找到了解决方案:

我的代码中的主要更改:

创建一个状态来跟踪执行

const [doAsyncCall, setDoAsyncCall] = useState(false);

在onClick事件中设置状态:

  const getItems = () => {
    return [
      {
        key: 'button1',
        name: 'Do something',
        cacheKey: 'button1', //
        onClick: () => setDoAsyncCall(true)
      }
    ];
  };

将异步功能包装在依赖于状态的 useEffect 部分:

  useEffect(() => {
    async function myAsyncMethod () {
      try {
        const responseData = (await get('some/data'))!.status;
        setApiResponseCode(responseData);
        setShowMessageBar(true);
      } catch (error) {
        console.error(error);
        setApiResponseCode(-1);
        setShowMessageBar(true);
      }
    }

    if (doAsyncCall) {
      myAsyncMethod();
    }
  }, [doAsyncCall]);