SWR 无法与异步提取一起正常工作

SWR not working properly with async fetch

最近更新了 SWR - 现在由于某种原因我的数据无法正确获取。

const { data: expressionsData, error: expressionsError } = useSWRImmutable(
       [`dashboard/expression/get-expression-analytics?startTime=${startDate}&endTime=${endDate}`, startDate, endDate],
       apiRequest
  );

使用这个抓取,

import firebase from "./firebase";

export async function apiRequest(path, method = "GET", data) {
  const accessToken = firebase.auth().currentUser
    ? await firebase.auth().currentUser.getIdToken()
    : undefined;
    //this is a workaround due to the backend responses not being built for this util.
  if (path == "dashboard/get-settings") {
    return fetch(`/api/${path}`, {
      method,
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${accessToken}`,
      },
      body: data ? JSON.stringify(data) : undefined,
    })
      .then((response) => response.json())
      .then((response) => {
        if (response.error === "error") {
          throw new CustomError(response.code, response.messages);
        } else {
          return response;
        }
      });
  }
  return fetch(`/api/${path}`, {
    method,
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${accessToken}`,
    },
    body: data ? JSON.stringify(data) : undefined,
  })
    .then((response) => response.json())
    .then((response) => {
      console.log("error", response);

      if (response.status === "error") {
        // Automatically signout user if accessToken is no longer valid
        if (response.code === "auth/invalid-user-token") {
          firebase.auth().signOut();
        }

        throw new CustomError(response.code, response.message);
      } else {
        return response.data;
      }
    });
}

// Create an Error with custom message and code
export function CustomError(code, message) {
  const error = new Error(message);
  error.code = code;
  return error;
}

// Check if a indexDb database exists
export function indexedDbdatabaseExists(dbname, callback) {
  const req = window.indexedDB.open(dbname);
  let existed = true;
  req.onsuccess = function () {
    req.result.close();
    if (!existed) window.indexedDB.deleteDatabase(dbname);
    callback(existed);
  };
  req.onupgradeneeded = function () {
    existed = false;
    callback(existed);
  };
}

现在我正在查看这个 Whosebug 线程,

useSWR doesn't work with async fetcher function

我想我会重新制作没有异步的 fetcher。我只是想知道为什么这通常会停止工作,以及我是否可以保留现有的代码库。

错误是一条 400 消息,它只发生在这个表达式 API 调用中,我认为由于数据量大,加载时间更长,

 xxxx/dashboard/expression/get-expression-analytics?startTime=1648183720488&endTime=1650865720488 400 (Bad Request)

有错误日志

这些调用工作正常,但它们的数据少得多。

const { data: overall, error: psychometricError } = useSWRImmutable(
    `dashboard/psychometric/get-psychometric-home?starttime=infinite`,
    apiRequest
  );

  const { data: sentimentData, error: sentimentError } = useSWRImmutable(
     [`dashboard/sentiment/get-sentiment-timefilter?startTime=${startDate}&endTime=${endDate}`, startDate, endDate],
    fetchSentiment
  );

对获取调用进行了更新,使其更具可读性,特别是关于 URL 路径。

import firebase from './firebase';

// Create an Error with custom message and code
export function CustomError(code, message) {
  const error = new Error(message);
  error.code = code;
  return error;
}

export async function expressionsRequest(path, method = 'GET') {
  const accessToken = firebase.auth().currentUser
    ? await firebase.auth().currentUser.getIdToken()
    : undefined;
  return fetch(`/api/${path}`, {
    method,
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    },
  })
    .then((response) => {
      if (!response.ok) {
        throw `Server error: [${response.status}] [${response.statusText}] [${response.url}]`;
      }
      return response.json();
    })
    .then((receivedJson) => {
      if (receivedJson.status === 'error') {
        // Automatically signout user if accessToken is no longer valid
        if (receivedJson.code === 'auth/invalid-user-token') {
          firebase.auth().signOut();
        }

        throw new CustomError(receivedJson.code, receivedJson.message);
      } else {
        return receivedJson.data;
      }
    })
    .catch((err) => {
      console.debug('Error in fetch', err);
      throw err;
    });
}

此外,这就是 lambda 函数(使用下一个 API 文件夹)的样子,

const requireAuth = require('../../_require-auth');
const { db } = require('../../_sql');

export default requireAuth(async (req, res) => {
  const { uid: id } = req.user;

  const startTime = Math.round(req.query.startTime * 0.001);
  const endTime = Math.round(req.query.endTime * 0.001);

  const parameters = [id, startTime, endTime];

  //sql injection definitely possible here, need to work out better method of dealing with this.
  const sqlText = `SELECT a,b,c,d,e,f,g,h,i FROM tablename WHERE a= AND i BETWEEN  AND ;`;

  try {
    const { rows } = await db.query(sqlText, parameters);
    return res.status(200).json({
      code: 0,
      data: rows,
    });
  } catch (error) {
    return res.status(200).json({
      code: 0,
      message: 'Error occurred in getting tablename',
      error,
    });
  }
});

使用具有相同查询的邮递员,即

curl --location --request GET 'http://localhost:3000/api/dashboard/expression/get-expression-analytics?startTime=1648387240382&endTime=1651069240382' \
--header 'Authorization: Bearer xxxx' \
--data-raw ''

成功 returns 附有数据的响应。

根据您的第一个代码块,startDate 值将作为 method 传递到提取器中,而 endDate 值将作为 [=14] 传递到提取器中=].这是基于关于为 key 参数传递数组的 useSWR 文档:https://swr.vercel.app/docs/arguments#multiple-arguments

如果您提供的代码是正确的,我会假设 400 是由于尝试为 method 选项传递一个随机值来获取。

这应该通过仅将 API 端点路径传递到 useSWR 而不是数组来解决:

const { data: expressionsData, error: expressionsError } = useSWRImmutable(
  `dashboard/expression/get-expression-analytics?startTime=${startDate}&endTime=${endDate}`,
  apiRequest
);