在 api 调用期间无法更新状态以更改页面上的显示

Can't update state to change display on page during api call

我有一个名为 Dashboard 的页面,在此页面上有一个名为 Dropzone 的组件,用于将文件上传到该页面。

上传文件时,它会调用 onDrop() 回调,我 POST 将文件发送到 api,然后我尝试向 [=] 发送 GET 请求36=] 在 /api/machines/{hash}。我想在此 GET 请求之前将我的 state.pcapAnalysing 变量设置为 TRUE,并在它完成后将其设置为 FALSE。这样做的想法是在仪表板页面上显示一条 'analysing' 消息,而 api 调用是 运行,这可能需要几秒钟才能到达 return。

由于状态未刷新,消息不会显示,因为状态从不更新以显示 state.pcapAnalysing 除了 false 之外的任何内容。有谁知道我怎样才能达到我想要的效果?本质上,我试图在 api 调用操作期间在 Dashboard 页面上显示一条消息,但它是由驻留在 Dropzone.js 中的组件触发的。谢谢。

Dashboard.js

...

export default function Dashboard() {

  const [currentTime, setCurrentTime] = useState(0);
  const [state, dispatch] = useContext(Context);

  useEffect(() => {
    fetch('/api/time').then(res => res.json()).then(data => {
      setCurrentTime(data.time);
    });
  }, []);

  return (
    <PageBase navigation={navigation}>
        <div className="flex place-content-center mt-10">
          <div className="flex w-1/2 place-content-center">
              { !state.pcapUploaded ? 
                state.pcapAnalysing ? 
                  <p>uploading</p> // Want to show this when state.pcapAnalysing is TRUE
                :
                  <TwoCard numcols="2">
                    <div>
                      <h5 className="">Upload a PCAP file to analyse</h5>
                      <p>Accepted file types: .pcap, .pcapng, .tcpdump</p>
                    </div>
                    <div className="mt-5 lg:ml-2 lg:mt-0 md:mt-0">
                      <MyDropzone/>
                    </div>
                  </TwoCard> 
              : 
                <TwoCard numcols="3">
                  <div className='col-span-1'>
                    <img src={require("../Assets/pcap.png")}/>
                  </div>
                  <div className='col-span-2'>
                    <h5 className="">Selected File:</h5>
                    <p className="break-all">{state.pcapFileName}</p>
                    <p className="break-all">{state.pcapHash}</p>
                    <button type="button" onClick={clearPcap}>
                      Change
                    </button>
                  </div>
                </TwoCard>
              }
          </div>
        </div>
        <div>
          { state.pcapUploaded ? <TileGrid tiles={tiles}/> : null } 
        </div>
      </PageBase>
  );
}

Dropzone.js

import { useCallback, useEffect, useContext } from 'react';
import { useDropzone } from 'react-dropzone';
import { Context } from '../Helpers/Provider';


export default function MyDropzone() {

  const [state, dispatch] = useContext(Context);

  const onDrop = useCallback(acceptedFiles => {
    const formData = new FormData();
    formData.append('file', acceptedFiles[0]);

    fetch('/api/upload',
      {
        method: 'POST',
        body: formData,
      }
    )
    .then(res => res.json())
    .then(result => {
      console.log('Success: ', result);
      console.log("dispatching...");

      dispatch({
        type: 'HASH',
        payload: result['hash'],
      });

      dispatch({
        type: 'ANALYSING', // Want to use this to set state.pcapAnalysing to TRUE
      });

      console.log("before: " + state.pcapAnalysing);

      if (state.machineIPs == null) {
        console.log("Machines: ", state.pcapHash);
        fetch(`/api/machines/${result['hash']}`)   // This request may take a few seconds
          .then(res => res.json())
          .then(data => {
            console.log(data);
            dispatch({
              type: 'MACHINES',
              payload: result,
            });
          });
      };
  
      dispatch({
        type: 'ANALYSING', // Want to use this to set state.pcapAnalysing to false after request completes
      });
  
      console.log("after: " + state.pcapAnalysing);
    })
  }, [state.pcapHash])

  const {getRootProps, getInputProps, isDragActive} = useDropzone({
    onDrop,
    maxFiles: 1,
    accept: '.pcap, .pcapng, .tcpdump'
  })

  return (
    <div {...getRootProps()} className="..<shortened>..">
      Upload PCAP
      <input {...getInputProps()} />
      {
        isDragActive ?
          <p>Drop the file here ...</p> :
          <p className="ml-2 mr-2">Drag 'n' drop a file here, or click to select file</p>
      }
    </div>
  )
}

看起来您应该在 /api/machines 调用的 then 关闭 内设置最终状态 。不是之后。

所以整个调用应该是这样的:

fetch('/api/upload',
      {
        method: 'POST',
        body: formData,
      }
    )
    .then(res => res.json())
    .then(result => {
      console.log('Success: ', result);
      console.log("dispatching...");

      dispatch({
        type: 'HASH',
        payload: result['hash'],
      });

      dispatch({
        type: 'ANALYSING', // Want to use this to set state.pcapAnalysing to TRUE
      });

      console.log("before: " + state.pcapAnalysing);

      if (state.machineIPs == null) {
        console.log("Machines: ", state.pcapHash);
        fetch(`/api/machines/${result['hash']}`)   // This request may take a few seconds
          .then(res => res.json())
          .then(data => {
            console.log(data);

            dispatch({
                type: 'ANALYSING', // Want to use this to set state.pcapAnalysing to false after request completes
            });

            dispatch({
              type: 'MACHINES',
              payload: result,
            });
          });
      };
    })
  }, [state.pcapHash])

注意我完全删除了那行

console.log("after: " + state.pcapAnalysing);

因为即使我在 then 关闭时移动它,状态也不会更新,因为组件 re-render 还没有发生。