Week 2 ~ Learning ~

terenvelan

TEREN VELAN

Posted on June 27, 2021

Week 2 ~ Learning ~

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:

  1. We need a form component for the initial state , where it takes in the user's email
  2. 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);
Enter fullscreen mode Exit fullscreen mode

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}`
      );
    }
  };
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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
)
Enter fullscreen mode Exit fullscreen mode

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>
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Using the similar login and component structure we are able to do the ResetPassword page in a similar way.

Learning Lessons:

  1. 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.

  2. 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)

  3. 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.

πŸ’– πŸ’ͺ πŸ™… 🚩
terenvelan
TEREN VELAN

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