繁体   English   中英

我的状态是第一次渲染中的空数组,登录后的空白页面

[英]my states are empty arrays in the first render, blank page after login

在我在 Android 上运行的 React Ionic 项目中,我希望能够登录并检查我作为用户是否同意我们的使用条款。 为此,我编写了以下函数,其中包括:

  • POST 请求到'/login' API 端点(第一个请求)
  • '/terms-of-use'端点的 GET 请求
  • '/accepted-terms-of-use'端点的 GET 请求,使用标头中第一个请求的response.data.token中的令牌
  • '/terms-of-use''/accepted-terms-of-use'并在“TermsOfUse.tsx”中呈现用户通过missingTermsofUse数组仍未接受的missingTermsofUse

如果missingTermsofUse数组的长度为0 ,则表示用户已经接受了所有使用条款,我们将他推送到homePage ,否则我们将他推送到TermsOfUse页面。

我正在使用 Context 以便在应用程序的任何地方使用道具

登录.tsx

const history = useHistory();

  const {
    termsofUse, 
        setTermsofUse,
        acceptedTermsofUse, 
        setAcceptedTermsofUse,
       missingTermsofUse, 
       setMissingTermsofUse
  } = React.useContext(MyContext);

    const doLogin = async (e: any) => {
        e.preventDefault();
    
        const loginData = {
          email: email,
          password: password,
        };
    
        // plugin: https://github.com/capacitor-community/http
        const options = {
          url: "https://xxx/login",
          headers: {
            Accept: "application/json",
            "Content-Type": "application/json",
          },
          data: loginData,
        };
       
    
        await Http.request({
          ...options,
          method: "POST",
        })
          .then((response: any) => {
            if (response.status == 201) {
              if (response.data.token) {
                localStorage.setItem("user",   JSON.stringify(response.data));
                const options1 = {
                  url: "https://xxx/terms-of-use",
                  headers: {
                    Accept: "application/json",
                    "Content-Type": "application/json",
                  },
                };
                const options2 = {
                  url: "https://xxx/accepted-terms-of-use",
                  headers: {
                    Accept: "application/json",
                    "Content-Type": "application/json",
                    Token: response.data.token,
                  },
                };
                Http.request({ ...options1, method: "GET" }).then(
                  (response1: any) => {
                    setTermsofUse(response1.data);
    
                    Http.request({ ...options2, method: "GET" }).then(
                      (response2: any) => {
                        setAcceptedTermsofUse(response2.data);

    //find terms of use which user has not accepted (entries which are present in response1 but not present in response2)

                        setMissingTermsofUse(
                          termsofUse.filter(
                            ({
                              id,
                              name,
                            }: {
                              id: number;
                              name: number;
                            }) =>
                              !acceptedTermsofUse.find(
                                (entry: any) =>
                                  entry.id == id && entry.name == name
                              )
                          )
                        );
                      }
                    );
                  }
                );
   
              }
    
    missingTermsofUse.length != 0 ? history.push("/termsOfUse") : history.push("/homePage");
            }
          .catch((error) => {
            console.log("Error:" + JSON.stringify(error));
          });
      };

登录后,我得到一个空白页,既不TermsofUse页,也没有homePage 你知道什么可能导致错误吗? 我认为第一个渲染包含null值,但我真的不知道如何重新编写请求以避免这种情况(如果这是导致问题的原因)。

我是 React 的新手,任何帮助将不胜感激。

编辑:

从日志中,我可以看到termsofUseacceptedTermsofUsemissingTermsofUse是空的。 这意味着在登录期间,它们在 Context.tsx 中设置的初始值被呈现。 我该如何解决这个问题?

我对 React 不是很了解,但我发现了一些与如何处理异步代码相关的潜在问题:

  1. 您同时使用 await 和 .then()
await Http.request({
      ...options,
      method: "POST",
    })
      .then((response: any) => {
...

我建议只坚持一种处理承诺的方法。 您可以在 google 上找到有关 async/await 与 then/catch 的大量信息,这是我认为已经很好解释的结果之一(不是我的): https : //medium.com/@dio.hamidou/async-await- vs-then-catch-4f64d42e6392

  1. 线
acceptedTermsofUse.length != 0 ? history.push("/termsOfUse") : history.push("/homePage");

将在您从 GET '/terms-of-use' 和 '/accepted-terms-of-use' 得到答案之前执行,因为它不在最内部的 .then() 中。 这可能是您得到空的missingTermsofUse的原因

希望这有助于进一步调试您的问题!

在承诺链的深处,您调用setMissingTermsofUse 如果我理解正确,该调用似乎假定termsofUse的值已更新(由于之前调用setTermsofUse )。 但是,在 React 中,对 set state 的调用是异步的,因此您的代码不能依赖它们立即发生。 我的猜测是您的doLogin函数正在关闭termsOfUse的陈旧值(可能是您将其初始化为空数组)——然后过滤该空数组,这自然只会给您一个空数组。

我认为您要么需要移动负责找出效果中“缺少使用条款”的逻辑(使用useEffect React 钩子),然后只有在更新termsofUseacceptedTermsofUse termsofUse时才激活效果。 effect 中的代码可以访问更新后的值(因为 React 在状态发生变化后运行 effect)。

或者,你已经进入了新的价值termsofUseacceptedTermsofUse你的内doLogin功能(来自请求您寄出)。 所以也许只是直接使用这些响应的数据来找出缺少的使用条款。

未经测试,但类似于:

const doLogin = async (e: any) => {
  e.preventDefault();

  const loginData = {
    dateOfBirth: birthday,
    postalCode: postalCode,
  };

  // plugin: https://github.com/capacitor-community/http
  const loginResponse = await Http.request({
    url: "https://xxx/login",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    data: loginData,
    method: "POST",
  });

  if (201 !== loginResponse.status || !loginResponse.data.token) {
    return console.log(
      "Response status indicates resource may not have been created or no token was included in response.",
      { response: loginResponse }
    );
  }

  localStorage.setItem("user", JSON.stringify(loginResponse.data));

  const token = loginResponse.data.token;

  const termsOfUseResponse = await Http.request({
    url: "https://xxx/terms-of-use",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    method: "GET",
  });
  setTermsofUse(termsOfUseResponse.data);

  const acceptedTermsOfUseResponse = await Http.request({
    url: "https://xxx/accepted-terms-of-use",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
      Token: token,
    },
  });
  setAcceptedTermsofUse(acceptedTermsOfUseResponse.data);

  const missingTermsOfUse = termsOfUseResponse.data.filter(
    ({ id, name }: { id: number; name: number }) =>
      !acceptedTermsOfUseResponse.data.some(
        (entry: any) => entry.id == id && entry.name == name
      )
  );
  setMissingTermsofUse(missingTermsOfUse);

  missingTermsOfUse.length !== 0
    ? history.push("/termsOfUse")
    : history.push("/homePage");
};

所做的主要更改是:

  • 扁平化嵌套的承诺链和异步逻辑,因此希望它更具可读性,尽管
    • 它缺少一些错误处理
    • 如果您在某些异步操作(即此处的 HTTP 请求)之后设置状态,我所看到的建议是,在继续设置状态之前,您应该首先检查组件是否仍处于挂载状态。
  • 在过滤缺少的使用条款时使用some array 方法(而不是find ),因为您只对布尔值感兴趣。
  • 使用响应中的数据:设置新状态并找出缺少的使用条款。
  • 路由/推逻辑现在依赖于missingTermsOfUse (你的代码是用acceptedTermsofUse ,这似乎与你的问题的这部分):

如果missingTermsofUse数组的长度为0,则表示用户已经接受了所有使用条款,我们将他推送到homePage,否则我们将他推送到TermsOfUse 页面。


旁注:您似乎在使用 TypeScript。 如果是这样,通常建议避免使用any ,因为您选择退出 TypeScript 提供的主要好处和安全性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM