Skip to content

Instantly share code, notes, and snippets.

@takeshy
Last active November 18, 2019 02:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takeshy/fb8e9a4a5bf8047afdd4ddb2fdeffa1f to your computer and use it in GitHub Desktop.
Save takeshy/fb8e9a4a5bf8047afdd4ddb2fdeffa1f to your computer and use it in GitHub Desktop.
promise custom hooks
import { Actions, AppState } from "./reducers/index";
import { useReducer, useMemo, useRef, useEffect } from "react";
import withPromise from "./withPromise";
const groupStart =
process.env.NODE_ENV !== "production"
? (val: string) => {
console.groupCollapsed("Action Type:", val);
}
: (val: string) => {};
const consoleLog =
process.env.NODE_ENV !== "production"
? (...val: any) => {
console.log(...val);
}
: (val: any) => {};
const groupEnd =
process.env.NODE_ENV !== "production"
? () => {
console.groupEnd();
}
: () => {};
function enchancedDispatch(
dispatch: React.Dispatch<Actions>,
sessionID: string
): React.Dispatch<Actions> {
return async function(action: Actions) {
await withPromise(dispatch, sessionID, action, groupStart);
return;
};
}
export function useReducerWithEnhancedDispatch(
reducer: React.Reducer<AppState, Actions>,
initialAppState: AppState,
...args: any
): [AppState, React.Dispatch<Actions>] {
let prevState = useRef(initialAppState);
const [state, dispatch] = useReducer(reducer, initialAppState, ...args);
const dispatchEnhanced = useMemo(() => {
return enchancedDispatch(dispatch, state.session_id);
}, [dispatch, state.session_id]);
useEffect(() => {
if (state !== initialAppState) {
consoleLog("Prev state: ", prevState.current);
consoleLog("Next state: ", state);
groupEnd();
}
if (prevState.current.session_id != state.session_id) {
localStorage.setItem("app_session_id", state.session_id);
}
prevState.current = state;
}, [state]);
return [state, dispatchEnhanced];
}
import "whatwg-fetch";
const withPromise = (
dispatch: any,
sessionID: string,
action: any,
logger: (val: string) => void
) => {
return new Promise((resolve, reject) => {
if (!action.payload || !action.payload.request) {
logger(action.types);
resolve(dispatch(action));
return;
}
const beginType =
typeof action.types === "string" ? action.types : action.types[0];
const successType =
typeof action.types === "string"
? `${action.types}_SUCCESS`
: action.types[1];
const failType =
typeof action.types === "string"
? `${action.types}_FAIL `
: action.types[2];
logger(beginType);
dispatch({ types: beginType, payload: action.payload });
let url = action.payload.request.url;
const type = url
.split("/")
.pop()
.replace(/\?.*/, "")
.match("feed")
? "rss+xml"
: "json";
// tslint:disable-next-line:no-console
let headers: { [key: string]: string } = { Accept: `application/${type}` };
if (url.match(process.env.REACT_APP_API_URL)) {
headers = {
...headers,
"X-APP-SESSIONID": sessionID
};
}
const params: RequestInit = {
credentials: "omit",
headers: headers as any,
method: ""
};
if (
action.payload.request.method &&
action.payload.request.method.match(/get/i)
) {
params.method = "get";
if (action.payload.request.data) {
url =
url +
"?" +
Object.keys(action.payload.request.data)
.map(
k =>
encodeURI(k) + "=" + encodeURI(action.payload.request.data[k])
)
.join("&");
}
} else {
params.method = action.payload.request.method;
const formData = new FormData();
Object.keys(action.payload.request.data).forEach(name => {
formData.append(name, action.payload.request.data[name]);
});
params.body = formData;
}
fetch(url, params)
.then(response => {
if (response.ok) {
if (type === "json") {
response.json().then(result => {
logger(successType);
resolve(
dispatch({
payload: { data: result, params: { ...params, url } },
types: successType
})
);
});
} else {
response.text().then(result => {
const domParser = new DOMParser();
const doc = domParser.parseFromString(result, "text/xml");
const rss = Array.from(doc.querySelectorAll("item")).map(q => ({
title: (q.querySelector("title") as HTMLElement).textContent,
pubDate: (q.querySelector("pubDate") as HTMLElement)
.textContent,
link: (q.querySelector("link") as HTMLElement).textContent
}));
logger(successType);
resolve(
dispatch({
payload: { data: rss, params: { ...params, url } },
types: successType
})
);
});
}
} else if (response.status === 403) {
logger(successType);
reject(dispatch({ types: "LOGOUT" }));
} else {
if (response.status === 400) {
response.json().then(json => {
logger(failType);
reject(
dispatch({
payload: {
error: {
message: json.message,
field: json.field,
status: response.status
},
params: { ...params, url }
},
types: failType
})
);
});
} else if (response.status === 403) {
window.location.reload();
reject();
} else {
logger(failType);
reject(
dispatch({
payload: {
error: {
message: response.statusText,
status: response.status
},
params: { ...params, url }
},
types: failType
})
);
}
}
})
.catch(xhr => {
logger(failType);
reject(
dispatch({
payload: {
error: { statusText: "fetch error" },
params: { ...params, url }
},
types: failType
})
);
});
});
};
export default withPromise;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment