Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repeated pattern in components interacting with models #32

Open
uier opened this issue Jan 9, 2023 · 8 comments · May be fixed by #79
Open

Repeated pattern in components interacting with models #32

uier opened this issue Jan 9, 2023 · 8 comments · May be fixed by #79
Labels
help wanted Extra attention is needed question Further information is requested

Comments

@uier
Copy link
Member

uier commented Jan 9, 2023

每個有做 POST/PUT/DELETE 的元件都會有這段 pattern 出現,可以討論是否適合包成 hook

至少 catch 那邊的邏輯可以抽出來

async function submit() {
isLoading.value = true;
try {
const { annId } = (
await api.Announcement.create({
...newAnnouncement,
courseName: route.params.name as string,
})
).data;
router.push(`/course/${route.params.name}/announcements/${annId}`);
} catch (error) {
if (axios.isAxiosError(error) && error.response?.data?.message) {
errorMsg.value = error.response.data.message;
} else {
errorMsg.value = "Unknown error occurred :(";
}
throw error;
} finally {
isLoading.value = false;
}
}

@uier uier added help wanted Extra attention is needed question Further information is requested labels Jan 9, 2023
@uier uier added this to NOJ Board Jan 9, 2023
@github-project-automation github-project-automation bot moved this to 🆕 New in NOJ Board Jan 9, 2023
@uier
Copy link
Member Author

uier commented Jan 9, 2023

for example

async function submit() {
  const [data, error] = await wrapper("my_promise")
}
async function wrapper(myPromise) {
  try {
    const data = await myPromise()
    return [data, null]
  } catch (error) {
    return [null, error?.response?.data?.message || "Unknown error"]
  }
}

@uier
Copy link
Member Author

uier commented Jan 20, 2023

Related to #42

@laporchen
Copy link
Contributor

我可以試著處理看看這一項

@uier
Copy link
Member Author

uier commented Feb 23, 2023

@laporchen 上次看這部分有什麼想法嗎?

我覺得初步可以先從抽出 catch (error) 這一塊開始,例如我想到的是改寫成 .then().catch() 然後 catch() 裡放一個 reuse 的 error handling function

@laporchen
Copy link
Contributor

This is what I worked on.

import axios from "axios";

interface RequestHandler<T extends Function> {
  callback: T;
  parameters: T extends (...args: infer U) => unknown ? U : never;
}
function axiosErrorWrapper(error: unknown): string {
  if (axios.isAxiosError(error) && error.response?.data?.message) {
    return error.response.data.message;
  }
  return "Unknown error occurred :(";
}
export async function axiosRequestWrapper<
  F extends Function,
  R extends F extends (...args: unknown[]) => infer R ? R : never,
>(req: RequestHandler<F>): Promise<[null, string] | [R, null]> {
  let res: R;
  try {
    if (req.parameters) {
      res = await req.callback(...req.parameters);
    } else {
      res = req.callback();
    }
  } catch (error) {
    const errorMsg = axiosErrorWrapper(error);
    return [null, errorMsg];
  }
  return [res, null];
}

/* now we can request like this now
const [res, error] = await axiosRequestWrapper({
  callback: () => {
    return { data: "hello" };
  },
  parameters: [],
});

if (error === null) {
  console.log("got res", res.data);
} else {
  console.log("got error :(", error);
}
*/

@uier
Copy link
Member Author

uier commented Feb 25, 2023

這是個蠻好的包裝,我之前想很久要不要這樣寫,直到我後來想到,我們現在在一個元件裡如果有這樣的 API Call
都會有兩個 ref,一個 isLoading 跟一個 errorMsg,搭配著這個 API Call 用

所以如果要這樣整個包起來(callback, parameters...)我會傾向用 composable (useXXX),然後連 isLoading 跟 errorMsg 都由 composable 提供,簡單來說會長得類似 useAxios

const { isLoading, error, execute } = useAsync(callback: Function)
function submit() {
  if ( isValidForm() ) execute(params);
}

@uier
Copy link
Member Author

uier commented Feb 25, 2023

總結一下,我傾向寫一個 useAsync 或先只改 error handling 的部分

@Bogay
Copy link
Member

Bogay commented Feb 26, 2023

最近在學 Nuxt.js,發現它的 useAsyncData 這設計應該就很符合我們的需求?但多了一個 refresh 用來重新抓資料,還有參數有個 watch 可以讓它 reactive,感覺是可以參考下。

附上官方範例:

const { data, pending, error, refresh } = await useAsyncData(
  'mountains',
  () => $fetch('https://api.nuxtjs.dev/mountains')
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed question Further information is requested
Projects
Status: 🆕 New
Development

Successfully merging a pull request may close this issue.

3 participants