user-slice.ts
export const login = createAsyncThunk(
'user/auth-login',
async(loginData: UserLogin, { rejectWithValue }) => {
try {
const res: any = await axios.post(POST_LOGIN, {
email: loginData.email, password: loginData.password });
return res.data;
}
catch(err: any) {
popup(err.response.data['err'], 'err');
}
}
)
export const save = createAsyncThunk(
'users/save-changes',
async(updatedUser: FormData, { rejectWithValue }) => {
try {
const res: any = await axios.post(POST_SAVE, updatedUser, {
headers: { ...constructHeaders(true, true) }
})
}
catch(err: any) {
popup(err.response.data['err'], 'err');
}
}
)
export const signUp = createAsyncThunk(
'user/auth-signup',
async(newUserData: FormData, { rejectWithValue }) => {
try {
const res: any = await axios.post(POST_SIGNUP, newUserData, {
headers: { ...HEADERS_FILE }
})
return res.data;
}
catch(err: any) {
popup(err.response.data['err'], 'err');
return rejectWithValue(err.response.data['err']);
}
}
)
Let's say I have these, instead of putting try/catch blocks for every single one of them, I want to centralize it to reduce code and just display a popup for the user, is it possible with just a class or does rtk have something specifically for this?
Related
I was working on a project using Firebase cloud messaging react. I was sending this to my server, but it doesn't work. Surely I have tried, but I don't know what's wrong again.
Below is the code.
Here it sends a POST request to Firebase, and it should send a notification to the user.
async function sendNotification(id, userMessage) {
const headers = {
'Authorization': `key=${code}`,
'Content-Type': 'application/json'
}
const message = {
'to': `${id}`,
'content_available': true,
'apns_priority': 5,
'notification': {
body: `${userMessage}`
},
const url = 'https://fcm.googleapis.com/fcm/send'
//console.log(code)
await axios.post(url, message, {
headers: headers
})
}
const sendMessageToServer = async (e) => {
//e.preventDefault();
toggle()
const docRe = doc(database, "help", mailer);
const data = {
email: user.email,
user: newMessage,
}
//console.log(data, 'not clear')
setNewMessage('')
//console.log(data, newMessage, 'cleared')
setShow(false)
if(newMessage === '') {
}
else {
const docRef = doc(database, "users", mailer);
await updateDoc(docRe, {
msg: arrayUnion(data)
})
.then(() => {
async function p() {
const id = await getDoc(docRef)
//console.log(id.data())
sendNotification(id.data().notice, `Admin : ${data.user}`)
}
p()
})
}
Sometimes it sends to my localhost because I tested there, but it doesn't work on my Netlify app. Secondly, I noticed that it keeps generating the same token for each user, but that's not the issue, but if you can help in both I would be grateful.
export default function Dashboard() {
async function callToken() {
await getToken(messaging, {vapidKey: process.env.NOTIFICATION})
.then((code) => {
//console.log(code)
async function docRef() {
const dc = doc(database, "users", auth.currentUser.email);
await updateDoc(dc, {
notice: code
});
}
docRef()
})
}
async function requestPermission() {
await Notification.requestPermission()
.then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.')
callToken()
}
})
}
const goTo = useNavigate();
useEffect(() => {
onAuthStateChanged(auth, (data) => {
if(!data) {
goTo('/login')
}
else {
currentBalance();
requestPermission()
}
})
})
}
Please know I imported all required modules.
I change the redux in my project to ReactQuery,and i got some problem with put req in my code.
this is my code
const { dispatch } = store;
const editClientDataAsync = async ( id,data ) => {
await axiosObj().put(`clients/${id}`,data);
}
const { mutateAsync: editClientData, isLoading } = useMutation(['editClientData'], editClientDataAsync, {
onSuccess: () => dispatch({ type: SUCCESS_DATA, payload: { message: "Success" } }),
onError: () => dispatch({ type: ERROR_DATA, payload: { message: "Error" } })
});
return { editClientData, isLoading }
}
same problem with when i try to get some req with id
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async ({ queryKey }) => {
const [_, { id }] = queryKey;
console.log(queryKey)
if (!id)
return;
const { data } = await axiosObj().get(`clients/${id}`)
console.log(data)
return data;
}
const { data: clientDetails, isLoading } = useQuery(['ClientId', { id }], getClientDetails)
return { clientDetails, isLoading }
Mutation functions only take 1 argument
Check where you use the editClientData mutation and pass the arguments in one object.
const editClientDataAsync = async ({ id, data }) => {
await axiosObj().put(`clients/${id}`,data);
}
return useMutation(['editClientData'], editClientDataAsync, ...);
Are you sure you get an id passed to the function?
You can disable the query until you get that id with the enabled option, so you don't make an unnecessary http call.
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async (id) => {
const { data } = await axiosObj().get(`clients/${id}`)
return data;
}
return useQuery(['client', id], () => getClientDetails(id), { enabled: !!id })
Disable/pausing queries
Before supabase V2 I got my data and stored in with redux like this:
supabase V2 broke unfortunately some functionality with redux-next-wrapper
export const getServerSideProps = wrapper.getServerSideProps(
(store) => async ({ req }) => {
const { user } = await supabase.auth.api.getUserByCookie(req);
if (user === null) {
return {
redirect: {
permanent: false,
destination: "/auth",
},
props: {},
};
}
if (user) {
async function getData() {
let { data, error, status } = await supabase
.from("table")
.select(`id`)
.eq("id", user.id);
store.dispatch(writeUserData(data));
return data;
}
return {
props: {
data: await getData(),
},
};
}
}
);
Any ideas how I can achieve the same functionality with withPageAuth()?
export const getServerSideProps = withPageAuth({
redirectTo: '/foo',
async getServerSideProps (ctx, supabase) {
// Access the user object
const {
data: { user }
} = await supabase.auth.getUser()
return { props: { id: user?.id } }
}
})
This would be similar to what you already have except that the redirect is handled by the withPageAuth already.
export const getServerSideProps = withPageAuth({
redirectTo: '/auth',
async getServerSideProps (ctx, supabase) {
// Access the user object
const {
data: { user }
} = await supabase.auth.getUser()
let { data, error, status } = await supabase
.from("table")
.select(`id`)
.eq("id", user.id);
store.dispatch(writeUserData(data));
return { props: { data } }
}
})
I have this HOC line of code from withUser.tsx. When a user is authenticated, the authenticated pages will then be wrapped by it so that the specified user-role will be the one to only have access to pages intended.
import axios, { AxiosError } from "axios";
import { API } from "../config";
import { getCookie } from "../helpers/auth";
const withUser = (Page: any) => {
const WithAuthUser = (props: any): JSX.Element => <Page {...props} />;
WithAuthUser.getInitialProps = async (context: any): Promise<any> => {
const token = getCookie("token", context.req);
let user = null;
let userLinks = [];
if (token) {
try {
const response = await axios.get(`${API}/user`, {
headers: {
authorization: `Bearer ${token}`,
contentType: "application/json",
},
});
console.log("Response in withUser: ", response);
user = response.data.user;
userLinks = response.data.links;
} catch (err: unknown) {
const error = err as AxiosError;
if (error.response?.status === 401) {
user = null;
}
}
}
if (user === null) {
// redirect
context.res.writeHead(302, {
Location: "/",
});
context.res.end();
} else {
return {
...(Page.getInitialProps ? await Page.getInitialProps(context) : {}),
user,
token,
userLinks,
};
}
};
return WithAuthUser;
};
export default withUser;
Now, the above code is not my final writing of TypeScript, I could be wrong but this is how I converted it from JS, please feel free to give me a refactored TSX codes here, here is the JS version:
import axios from "axios";
import { API } from "../config";
import { getCookie } from "../helpers/auth";
const withUser = (Page) => {
const WithAuthUser = (props) => <Page {...props} />;
WithAuthUser.getInitialProps = async (context) => {
const token = getCookie("token", context.req);
let user = null;
let userLinks = [];
if (token) {
try {
const response = await axios.get(`${API}/user`, {
headers: {
authorization: `Bearer ${token}`,
contentType: "application/json",
},
});
console.log("Response in withUser: ", response);
user = response.data.user;
userLinks = response.data.links;
} catch (error) {
if (error.response.status === 401) {
user = null;
}
}
}
if (user === null) {
// redirect
context.res.writeHead(302, {
Location: "/",
});
context.res.end();
} else {
return {
...(Page.getInitialProps ? await Page.getInitialProps(context) : {}),
user,
token,
userLinks,
};
}
};
return WithAuthUser;
};
export default withUser;
But now, when using it when an Authenticated /user page, I could not get any data from the user. It will give me an undefined and for example, user.first_name will not be shown:
import withUser from "../withUser";
const User = ({ user }: any): JSX.Element => (
<div className="flex min-h-screen flex-col items-center justify-center">
{user.first_name}
</div>
);
export default withUser(User);
Any correct ways of implementing this would be very much appreciated. Thanks!
Whether you are in Reactjs or Nextjs, I think there needs to have a correct type definitions of your HOC component in the first place.
First you need to define your HOC component as a React.ComponentType:
const withUser = (ChildComp: React.ComponentType<any | string>) => { /* code follows */ }
you also need to define an interface for the expected values for these"
const token = getCookie("token", context.req);
let user = null;
let userLinks = [];
and when you wrap your child component, say user.tsx, do it like this:
type UserType = {
first_name: string
}
const User: React.SFC<ContainerProps> = ({ user}: UserType)=> (
<h1>{user.first_name ?? "User not found"}</h1>
);
export default withUser(User);
You can read more about here: Create a TypeScript HOC in React
Okay, sorry this was just a bug and I figure out that I did not have any userLinks from the REST API that I was passing in. So I can already consider this question as resolved as I have already fixed it.
Here is the code of my fix:
import axios, { AxiosError } from "axios";
import { API } from "../config";
import { getCookie } from "../helpers/auth";
const withUser = (Page: any) => {
const WithAuthUser = (props: any): JSX.Element => <Page {...props} />;
WithAuthUser.getInitialProps = async (context: any): Promise<any> => {
const token = getCookie("token", context.req);
console.log("token: ", token);
let user = null;
if (token) {
try {
const response = await axios.get(`${API}/user`, {
headers: {
authorization: `Bearer ${token}`,
contentType: "application/json",
},
});
console.log("response: ", response);
user = response.data;
} catch (err: unknown) {
const error = err as AxiosError;
if (error.response?.status === 401) {
user = null;
}
}
}
if (user === null) {
// redirect
context.res.writeHead(302, {
Location: "/",
});
context.res.end();
} else {
return {
...(Page.getInitialProps ? await Page.getInitialProps(context) : {}),
user,
token,
};
}
}
return WithAuthUser;
}
export default withUser;
I'm trying to dispatch more than one axios request inside my method. However, it is not working.
export const getImages = (res) => {
return {
type: actionTypes.GET_IMAGES,
payload: res
}
}
export const loadImages = (imgs, cId) => {
return dispatch => {
let data = [];
for(const i of imgs) {
const id = i.id;
axios.get(`${api.URL}/test/${cId}/files/${id}`)
.then(res => {
if(res.data !== -1) {
const obj = {
name: res.data,
desc: i.caption
};
data(obj);
}
//dispatch(getImages(data));
});
}
console.log('Action:');
console.log(data);
dispatch(getImages(data));
}
}
The console log does not print anything. Do I need to dispatch inside the .then()? If so, how can I run multiples requests before dispatching?
Thanks