Week 2 ~ Learning ~
TEREN VELAN
Posted on June 27, 2021
What this ~ Learning ~ series will be is essentially the recap of what i've learnt in that week.Starting from Week 2 at my Traineeship.
Week 2 (21 Jun 21 - 25 Jun 21)
So for Week 2 i worked on the front-end for the forgot password and reset password page. Where i built the components for it.
First we look at the forgot password page:
- We need a form component for the initial state , where it takes in the user's email
- On submit , we face two possible scenarios, Success or Error. And for each scenario we will need components to display the messages accordingly.
Taking it step at a time , we look at point 1. Once we have the form , we create a function to simulate an API call and the results we get back.
const [pageDetails, setPageDetails] = useState({
error: null,
success: null,
});
//to simulate an API
//two test cases , success or error
let runForgotPassword = () => {
// simulate error
// let response = {
// error: {
// header: "Invalid Email!",
// message:
// "We are unable to locate your email address in our database.If you have not registered with us, please do so at our sign up page.",
// },
// success: null,
// };
let response = {
success: {
header: "Email Sent!",
message:
"A verification email has been sent your email account.Please follow the instructions in the email to change your password.",
},
error: null,
};
setPageDetails(response);
To explain , after setting the setPageDetails(response) and changing its state , we want to preserve the values of header and message.
But why you ask?
In the case where the user clicks refresh , the state will return back to its original render and display the Form component from point 1. This can cause hackers to use bots to submit spam emails and overload the api calls. To make sure this does not happen , we run a useEffect to ensure a check it done on the state , checking if the query is "success=true" or "success=false".
let runForgotPassword = () => {
// simulate error
// let response = {
// error: {
// header: "Invalid Email!",
// message:
// "We are unable to locate your email address in our database.If you have not registered with us, please do so at our sign up page.",
// },
// success: null,
// };
let response = {
success: {
header: "Email Sent!",
message:
"A verification email has been sent your email account.Please follow the instructions in the email to change your password.",
},
error: null,
};
setPageDetails(response);
try {
//encodeURIcomponent is a JS function for encoding text into URL appropriate text
let message = encodeURIComponent(
response?.success?.message || response?.error?.message
);
let header = encodeURIComponent(
response?.success?.header || response?.error?.header
);
//router.replace is a nextjs function
//Here we replace the url with the response header and message so as to preserve that data even on refresh.
router.replace(
`/forgot?success=${
!!response.success ? "true" : "false"
}&header=${header}&message=${message}`
);
} catch (err) {
let message = "Some Generic success message";
let header = "Some Generic success header";
router.replace(
`/forgot?success=true&header=${header}&message=${message}`
);
}
};
So following that , on refresh , the page will be hydrated with the data stored in the query param as seen below:
let router = useRouter();
let { query } = router;
// hydrate the state on load
useEffect(() => {
if (query?.success === "true") {
try {
//here we decode the message and use that to set the state for PageDetails
setPageDetails({
success: {
header: decodeURIComponent(query?.header),
message: decodeURIComponent(query?.message),
},
});
} catch (error) {}
} else if (query?.success === "false") {
try {
setPageDetails({
error: {
header: decodeURIComponent(query?.header),
message: decodeURIComponent(query?.message),
},
});
} catch (error) {}
}
}, [query]);
So then how does react know which component to display?
With a simple conditional statement using our pageDetails state. Because we used an object for our state , the data structure becomes more dynamic.
if (!!pageDetails?.error) {
return <ErrorContentSection error={pageDetails?.error || {}} />;
} else if (!!pageDetails?.success) {
return <SuccessContentSection success={pageDetails?.success || {}} />;
} else {
return (
//return the form input that receives users email
)
Below is an example of a component for one such scenario , success.
const SuccessContentSection = (props) => {
const { header, message } = props.success;
return (
<>
<LoginHeader title="App" />
<div className={styles.content}>
<Grid className={styles.grid}>
<Row className={styles.row}>
<Column className={styles.column} md={{ offset: 2, span: 4 }}>
<div className={styles.formWrapper}>
<div className={styles.formTitle}>{header}</div>
<div className={styles.createAccount}>{message}</div>
<div className={styles.layoutTop}>
<Link href="/">
<Button
className="submit"
// renderIcon={loading ? LoadingButton : ArrowRight16}
renderIcon={ArrowRight16}
type="submit"
>
Proceed to Login
</Button>
</Link>
</div>
</div>
</Column>
</Row>
</Grid>
</div>
</>
);
};
Using the similar login and component structure we are able to do the ResetPassword page in a similar way.
Learning Lessons:
Always anticipate what the users might do , in the case where the user might refresh and try to spam the API calls , we corrected that by breaking the loops of forgotPasswordPage > success/error message > press refresh > back to forgotPasswordPage. Now despite the refresh , the page still reads the query and hydrates the components based on that.
A way to ensure React remembers the state between renders/refresh is to use the "query" function from NextRouter where the data can be stored in the URL. (NOT FOR SECURE INFORMATION)
Always try to think of ways to make the page as dynamic as possible , as little hard coding as possible.
4.Think flexibly, Don't over-engineer it , neither should you under-engineer it.
Posted on June 27, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 30, 2024