<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tyrel Chambers</title>
    <description>The latest articles on Forem by Tyrel Chambers (@imtyrelchambers).</description>
    <link>https://forem.com/imtyrelchambers</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F192536%2Fd314ac96-2815-4494-8dfe-54c29e598493.png</url>
      <title>Forem: Tyrel Chambers</title>
      <link>https://forem.com/imtyrelchambers</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/imtyrelchambers"/>
    <language>en</language>
    <item>
      <title>Networking to get your first web-development job</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Sun, 17 Apr 2022 14:22:01 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/networking-to-get-your-first-web-development-job-1l9j</link>
      <guid>https://forem.com/imtyrelchambers/networking-to-get-your-first-web-development-job-1l9j</guid>
      <description>&lt;p&gt;Getting your first web development job can certainly be a daunting task. Never mind trying to learn how to program and the ins and outs of writing the code. Landing your first web development job presents its own unique set of challenges.&lt;/p&gt;

&lt;p&gt;I know this topic has been beaten to death in the tech industry, but it never hurts to have another perspective and I'm hoping I can shed some light on my process and hopefully help you too. It hasn't been easy, but I feel like that's not always the case, this is just my journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't believe the job description
&lt;/h2&gt;

&lt;p&gt;If you've looked at job boards, you've likely seen a job posting that goes something like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Junior web developer&lt;/p&gt;

&lt;p&gt;Must know React, Vue, Angular, Node, DevOps, DevRel, design systems, UX/UI&lt;/p&gt;

&lt;p&gt;1,300 years of experience in being a CEO&lt;/p&gt;

&lt;p&gt;Salary: $3.50&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, that is not a real description, but it's close enough to what job descriptions &lt;em&gt;feel&lt;/em&gt;. They usually have requirements that seem unrealistic and taken at face-value, they are.&lt;/p&gt;

&lt;p&gt;A lot of the time job descriptions aren't written by developers. Every company is different, obviously, but most of the time it's written by someone from HR without knowledge of what is needed; it's sort of like a perfect scenario type of thing.&lt;/p&gt;

&lt;p&gt;Job descriptions can be very intimidating though, especially when you're trying to get your first job. You'll sit there and look at it and think there's no way you could fill all those roles, but you're not alone. Everyone has felt that way.&lt;/p&gt;

&lt;p&gt;The important thing to remember is &lt;strong&gt;you're not expected to know everything&lt;/strong&gt;, if you do, you're probably over-qualified and should be looking for something else.&lt;/p&gt;

&lt;p&gt;It's also a good idea to apply for jobs where you don't quite know everything because that will allow you to learn something new.&lt;/p&gt;

&lt;p&gt;If you ask anyone, they will tell you the same thing: &lt;strong&gt;apply anyway&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Networking over applying
&lt;/h2&gt;

&lt;p&gt;I've sent in my fair share of resumes and been through a handful of interviews (even ghosted a few too due to anxiety). Anxiety was a large part of my journey and why it took me so long to get my first job. It wasn't the only reason. I spent a lot of the time learning and building my own projects. One of which is in production and being used by a handful of people.&lt;/p&gt;

&lt;p&gt;What did work wonders, however, was networking; especially on Twitter.&lt;/p&gt;

&lt;p&gt;Networking can happen anywhere: you could go to a meetup, introduce yourself and engage on social media, blog. Where you can find people to talk to, you can find opportunities to network.&lt;/p&gt;

&lt;p&gt;I've seen many, many people, get jobs because they were active on Twitter and networked with developers in their field. Not only do you meet like-minded individuals who share the same interests as you, but you also get your name in front of people.&lt;/p&gt;

&lt;p&gt;One of my Twitter friends encouraged me to send in a resume to the company he worked for and that he would make the necessary introductions to get me an interview. I was excited, but a little hesitant because I had been down this road before, so I wasn't sure how it would play out, but wouldn't you know it, it paid off.&lt;/p&gt;

&lt;p&gt;I'm incredibly thankful to him for giving me the chance and opportunity to get into the field. However, that would be a post for a different day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build your confidence
&lt;/h2&gt;

&lt;p&gt;When you network with other developers and business owners, you give yourself the opportunity to build confidence in yourself and what you know.&lt;/p&gt;

&lt;p&gt;I had an interview with a fast-growing company and one whose product I'd used in the past. We had some pretty good conversations, but ultimately it didn't go anywhere and I didn't get this interview by networking with anyone.&lt;/p&gt;

&lt;p&gt;What I did get though, is a new developer to connect with on Twitter. Even though I didn't get the job, I asked for feedback so I could increase my chances next time.&lt;/p&gt;

&lt;p&gt;He gave me amazing feedback that really helped me out and followed me on Twitter (other platforms) too. From this interview opportunity, I gained a new internet friend - a person to connect with, and another opportunity to network.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, what's next?
&lt;/h2&gt;

&lt;p&gt;We've sort of talked about the benefits with networking and my personal experience in the matter, but I'd like to layout some practical examples of networking on social media that I believe helped me, and what might help you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a social media presence. I would suggest Twitter or LinkedIn.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Follow people in your space and interact frequently in a polite and friendly way. You don't want to network with the sole purpose of getting a job or making it obvious that's why you're here. First and foremost, you're here to meet like-minded people and make friends - anything beyond that, is gravy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a blog. This gives you content to share with your network and let's them know, you know, what you're talking about (and it's generally just fun).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid hard-selling or self-promoting often. I think it's important to have a healthy balance between self-promotion and genuinely interacting with others. If in every interaction you're trying to push your brand new &lt;em&gt;this&lt;/em&gt; or your awesome &lt;em&gt;that&lt;/em&gt;, people are going to see that and form ideas about your intentions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be on your best behaviour: everything you post online, stays online.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Don't give up
&lt;/h2&gt;

&lt;p&gt;I know that sending in endless resumes can get tiring after a while. And, there are a lot of factors that are at play when you're applying for jobs, but don't give up on the networking aspect.&lt;/p&gt;

&lt;p&gt;Let me know on Twitter at &lt;a href="https://twitter.com/imtyrelchambers"&gt;@imtyrelchambers&lt;/a&gt; if this post has helped you or if networking helped get you your first job.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
    </item>
    <item>
      <title>How to avoid "hobby" burnout</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Tue, 08 Mar 2022 02:33:52 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/how-to-avoid-hobby-burnout-3fc9</link>
      <guid>https://forem.com/imtyrelchambers/how-to-avoid-hobby-burnout-3fc9</guid>
      <description>&lt;p&gt;It's often looked down upon when one values rest over constant hard work. At least, that's how it's been for as long as society has existed. However, things are changing.&lt;/p&gt;

&lt;p&gt;This topic is closely related to any topic around "burn-out", "overworking", and "just generally working so much you forget your own reflection and your friends forget you exist". And I'm sure you've experienced these things before too. That feeling you get when you're tired of doing what you used to love and thinking about doing that in the future, puts a weird feeling in your stomach.&lt;/p&gt;

&lt;p&gt;We've all dealt with this more than once in our lives. I've experienced it often enough to know how to avoid it, or at least keep it at bay and I'd like to share my process with you. I'm hoping it will help someone out there!&lt;/p&gt;

&lt;p&gt;This is tailored more to programming than other hobbies, but nevertheless, the points here are transferrable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A little back story
&lt;/h2&gt;

&lt;p&gt;I've been programming for the better part of 8-9 years at the time of writing this (2022, future me). I've been programming for a while with tutorials, guides, courses, and just my own side-projects. I've tried to have other hobbies to occupy my time, but there's nothing quite like building your own little apps... anyways.&lt;/p&gt;

&lt;p&gt;I've hit burnout more times than I can count and it's scary! I hate the feeling I get when I've clearly overworked and haven't taken breaks when my body was telling me too. Over the years, I've become much better at knowing when my body is telling me to take a break and I've become much better at listening to it.&lt;/p&gt;

&lt;p&gt;Once I broke out of that mentality that told me I can't take break or else the world would fall apart, that's when I truly found freedom. Freedom from having to constantly perform, and freedom to rest.&lt;/p&gt;

&lt;p&gt;In reality, if you take a day or two, even a week, from programming or whatever hobby you tend to do on a daily basis, for the most part, it's not going to hurt you. Life is short so be sure to take care of yourself and the things you love.&lt;/p&gt;

&lt;h2&gt;
  
  
  You don't have to program when you don't feel like it
&lt;/h2&gt;

&lt;p&gt;I always thought if I took a day off, I'd be behind and my career would be pushed back further and further. Believing that, was a mistake, but I didn't know any better. I always thought that I had to be programming everyday, all the time, but I was wrong and all that led to were month long breaks and a lot of stress.&lt;/p&gt;

&lt;p&gt;In about 2017/2018, I took up narrating thriller stories on my Youtube channel &lt;a href="https://youtube.com/storiesaftermidnight"&gt;Stories After Midnight&lt;/a&gt;. I uploaded a video every few days for a long while. To date I haven't uploaded in two months for a few different reasons, but the biggest among them is burnout and a lack of desire to create that type of content. But, for the past year (before I quit/took a break) I had been dealing without burnout for a while. Reading stories felt like a chore and something I &lt;em&gt;had to do&lt;/em&gt; every other day.&lt;/p&gt;

&lt;p&gt;The problem with Youtube is, people expect regular content, at least in my particular genre. Dealing with burnout in a content-creator sort of way is a different kind of animal. There's a lot of pressure to keep creating content and pumping out new things regardless of how you feel. On the other hand though, a vast majority of people are forgiving and most encourage breaks and times of rest!&lt;/p&gt;

&lt;p&gt;But after 370 about 1,000 stories read, I've taken a break; not that it matters in this context haha.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I avoid burnout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stop as soon as you feel like it's a chore
&lt;/h3&gt;

&lt;p&gt;I've gotten to the point where if I don't feel like programming even a little bit, I'll close my laptop even if in the middle of something, or I just won't look at my laptop at all. Whereas I used to just program anyways, now, I don't ever do that unless in important circumstances. I've found that this gives me the freedom to back away without guilt and live to love my hobby another day. Taking breaks no matter if I'm in the middle of something, or not, preserves my love for the craft and my sanity. I genuinely come back to it the next day or two, happy to be back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find other hobbies to fill your time (optional)
&lt;/h3&gt;

&lt;p&gt;I also have other hobbies related to programming which helps me further my "cause" without actually partaking in the hobby. I have books that I read that relate to programming or the mind, building a startup, or running social media. They all interest me and are applicable to my field. I'd encourage you to find something else related to what you enjoy that you can do when you need a rest. Even if you don't have anything, don't feel bad either, just take that break and enjoy it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Work on projects that excite you
&lt;/h3&gt;

&lt;p&gt;I've also found that projects can become stale in their excitement. I have a few different projects that are unfinished and usually serve as a distraction from the main project I'm working on. It's nice to have a few different ideas on the go as you can hop between them when one is feeling boring. I'd encourage this, so long as it works for you. Whatever project you can find that excites you, start it and take it as far as you want; don't be afraid to take a break or switch off that one either.&lt;/p&gt;

&lt;h3&gt;
  
  
  Just take a break
&lt;/h3&gt;

&lt;p&gt;This is also an option. Don't do anything. Turn on Netflix. Nap for 8 hours. Don't think about your hobby.&lt;/p&gt;

&lt;h2&gt;
  
  
  I hope this has helped
&lt;/h2&gt;

&lt;p&gt;In the end, you have to take care of yourself; there really isn't any way around that. If you're not taking care of yourself, the things you enjoy doing will start suffering. So when you're feeling burnt out and your hobby feels like a chore, step back, consider the points above, and decide the best move forward. This does come with a caveat. In the past, I have felt burnout for weeks to months and sometimes I've had to overcome that feeling by making myself do what caused the burnout to begin with. I've found this can be difficult to do, but I've also found that more often than not, the love comes back. Your milage may vary, so listen to your heart and consider the options.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>mentalhealth</category>
    </item>
    <item>
      <title>How I transferred Postgres data using Sequelize</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Fri, 04 Mar 2022 02:37:34 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/how-i-transferred-postgres-data-using-sequelize-43lc</link>
      <guid>https://forem.com/imtyrelchambers/how-i-transferred-postgres-data-using-sequelize-43lc</guid>
      <description>&lt;p&gt;Hey there!&lt;/p&gt;

&lt;p&gt;Have you ever asked yourself "How do I transfer data from one database to another without actually putting any real effort into it?". Me too.&lt;/p&gt;

&lt;p&gt;It would be nice if there was a tool that could allow us to interact and mutate data using a GUI because I can't write SQL to save my life.&lt;/p&gt;

&lt;p&gt;In this article I'm going to talk to you about how I transferred data from one database to another without exporting/importing the data. I had tried numerous ways using different export options within pgAdmin, but was generally unsuccessful. Growing frustrated I decided to look outside the box and try something different entirely. This will be a general high-level overview of how I did this, but not necessarily a step-by-step since you might not be using the same tools as I and I'm not a pro at pgAdmin or exporting SQL data.&lt;/p&gt;

&lt;p&gt;The new database had significant architectural changes, so a simple export/import wouldn't have been good enough (I'm also not that great at writing my own SQL to be able to transform it anyway).&lt;/p&gt;

&lt;h2&gt;
  
  
  An introduction
&lt;/h2&gt;

&lt;p&gt;I recently remade one of my first production applications. The reason for the rewrite was I had grown in my knowledge of programming overall and felt the current version of my app was flaky at best. This database wasn't big by any means, but it was being used in production and the last thing you want to do is lose data, somehow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Sequelize to connect to both databases
&lt;/h2&gt;

&lt;p&gt;This is probably a risky play if you're not careful because one brain-fart or wrong move and you could do some serious damage to both databases and your emotions.&lt;/p&gt;

&lt;p&gt;Within my &lt;code&gt;docker-compose&lt;/code&gt; file, I created another database that was blank. I named it something random because the name isn't important since it's temporary. I exported the production database and imported it to my replica database; including the commands to create everything from scratch.&lt;/p&gt;

&lt;p&gt;Once this data was successully important, I created a new sequelize instance in my &lt;code&gt;index.js&lt;/code&gt; file within my models folder that sequelize can generate for you. I named it something crazy like &lt;code&gt;sequelize2&lt;/code&gt; and gave it the value of a postgres uri string with the proper credentials to connect to that database.&lt;/p&gt;

&lt;p&gt;I find the benefits of doing it this way are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No risk of data loss in your production database by botched code&lt;/li&gt;
&lt;li&gt;Not having to deal with foreign keys in export&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I thought 'why not use the tools I already know how to use?'&lt;/p&gt;

&lt;p&gt;Technically, you could straight connect to your production database and go rogue and silly, but hey, if that's you, Godspeed!&lt;/p&gt;

&lt;h2&gt;
  
  
  We're connected to the replica database
&lt;/h2&gt;

&lt;p&gt;Alright, we're here. I'm going to layout some of my code so you can have an idea of my approach to the actual migration process. Make sure you have two connections setup, mine look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let sequelize2;

sequelize2 = new Sequelize(process.env.OLD_REDDEX_DB);
db.sequelize2 = sequelize2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When it's setup this way, we can access our replica database by referencing &lt;code&gt;db.sequelize2&lt;/code&gt; and our main database &lt;code&gt;db.sequelize&lt;/code&gt;. This will allow us to easily pull from one database and insert in another. Now, I don't know if this is "the best way to do it 🙄", but it worked for me and I'd do it again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the query function
&lt;/h3&gt;

&lt;p&gt;I created a function for query each table I wanted to include in the migration. All it did was fetch the data from the replica database and return it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;./migrate.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const getUsers = async () =&amp;gt; {
// sql statement to return all users
const sql = `SELECT email,uuid,password,initial_message,repeat_message,website_id,youtube_id FROM users`;
// return all users
const users = await db.sequelize2.query(sql, { type: QueryTypes.SELECT });

return users;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, this queries the data held in my &lt;code&gt;users&lt;/code&gt; table and returns it to me. However, we now need to mutate that data to fit our new tables and overall database structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mutating the data
&lt;/h3&gt;

&lt;p&gt;Next up, I created a function to receive the data return by the function above and mutate it to fit the new table structure.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;./migrate.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const migrateUsers = async () =&amp;gt; {
  const users = await getUsers();

for (let i = 0; i &amp;lt; users.length; i++) {
const user = users[i];
const {
email,
uuid,
password,
initial_message,
repeat_message,
website_id,
} = user;

    const newUser = await db.User.create({
      uuid,
      email,
      password,
      email_confirmed: true,
    });

    await newUser.createProfile({
      greeting: initial_message,
      recurring: repeat_message,
      words_per_minute: 0,
    });

    if (website_id) {
      const website = await db.Website.create({
        uuid: website_id,
        userId: newUser.uuid,
      });

      newUser.websiteId = website.uuid;

      newUser.save();
    }

}
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can use Sequelize to do what I need in order to properly mutate that data and make it fit in my database. The only thing that changed was the columns names in some tables and generally cleaning up unused or improperly named columns. I find that leaving tech debt like this (the improperly named columns and poor layout) can create issues down the road when you try to work with that data. In the new database, I made sure to take care of that from the start.&lt;/p&gt;

&lt;h3&gt;
  
  
  Executing the functions
&lt;/h3&gt;

&lt;p&gt;At the end of the file, I created a general &lt;code&gt;main()&lt;/code&gt; function that executes the functions above, in order. Any functions that query and return data, are listed within their respective mutation functions. I found this worked really well for me because if one function failed, it didn't mess with anything outside it's functional scope. Breaking down the process into smaller functions is just good practice in general, anyway.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;./migrate.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const main = async () =&amp;gt; {
  // await migrateUsers();
  // await migrateWebsites();
  // await migrateTags();
  // await migrateSubmittedStories();
  // await migrateRecentlySearched();
  // await migrateContacts();
  // await migrateContacted();
  await migrateStories();
};

main();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had multiple functions targetting and mutating multiple different columns. You can just add whatever you need in order to complete the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now that we have our data, we've mutated it to suit our needs, you're good to go.&lt;/p&gt;

&lt;p&gt;What I did was I entered my docker container that contained both databases. I then ran my &lt;code&gt;migrate.js&lt;/code&gt; file or whatever you named it, and let it do the rest.&lt;/p&gt;

&lt;p&gt;I hope this has helped give you an overall view on how to do something in a bit of an unorthodox way (in my opinion). It didn't make a whole lot of sense to try and do it in a way I was unfamiliar with, instead I just used the tools I was given.&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why do I Use Custom React Hooks?</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Mon, 20 Sep 2021 02:05:49 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/why-do-i-use-custom-react-hooks-4c7c</link>
      <guid>https://forem.com/imtyrelchambers/why-do-i-use-custom-react-hooks-4c7c</guid>
      <description>&lt;p&gt;If you're like me, you know hooks exist and you know you can make your own, but you might not be sure how to do that, or why. In this article, I'm going to try to explain when you can use custom React Hooks to clean up your code and abstract your state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do I use custom hooks?
&lt;/h2&gt;

&lt;p&gt;Custom React Hooks are fantastic at abstracting your state away from any one component and allowing other components to tap into that data. I've refactored a few large apps of mine using custom hooks. A huge benefit of this refactor was being able to share my state across a couple components without worrying about messing it up.&lt;/p&gt;

&lt;p&gt;Having your state encapsulated in its own function ensures that wherever that state is used, it'll be consistent. You won't have to worry about passing state down through props to child components, just call the function!&lt;/p&gt;

&lt;h2&gt;
  
  
  Make sure your custom hook begins with "use"
&lt;/h2&gt;

&lt;p&gt;When you're building your own React Hook, our hooks must begin with "use". As silly as it may sound sometimes, that's how it's done. If you're building a hook to store the current user, you would create your own &lt;code&gt;hooks&lt;/code&gt; folder and inside it, you would have &lt;code&gt;useUser.js&lt;/code&gt; which would also be the name of the hook itself!&lt;/p&gt;

&lt;p&gt;I like to show examples of what I'm talking about because that's just how I learn.&lt;/p&gt;

&lt;p&gt;Let's look at a barebones &lt;code&gt;useUser&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const useUser = () =&amp;gt; {
  const [currentUser, setState] = useState()

  const setUser = data =&amp;gt; {
    setState(data)
  }

  return {
    currentUser,
    setUser
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we want to take advantage of this custom hook, inside the component you want to access this state, you would include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const SomeComponent = () =&amp;gt; {
  const {currentUser} = useUser()
  /* code */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  You can pass in an initial state too
&lt;/h2&gt;

&lt;p&gt;A cool thing about writing a custom React Hook is, among other things, you can pass in an initial state, just like you can with the useState hook (because useState is just a hook anyway).&lt;/p&gt;

&lt;p&gt;I'm not sure if it necessarily needs to be said, but, here is how we would initialize our state in our custom React Hook with existing data.&lt;/p&gt;

&lt;p&gt;Copying our previous example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const useUser = (initialState) =&amp;gt; {
  const [currentUser, setState] = useState(initialState)

  const setUser = data =&amp;gt; {
    setState(data)
  }

  return {
    currentUser,
    setUser
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we pass our hook some data that gets passed to useState.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const SomeComponent = () =&amp;gt; {
  const {currentUser} = useUser({
    name: "Tyrel"
  })
  /* code */
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now when our &lt;code&gt;currentUser&lt;/code&gt; state is initialized, it will have &lt;code&gt;{name: "Tyrel"}&lt;/code&gt; already existing inside its state.&lt;/p&gt;

&lt;p&gt;This makes it pretty easy to say pull data from an API, then pass it into a custom hook with some other functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  You don't always need a custom hook
&lt;/h2&gt;

&lt;p&gt;I'm no expert, but you don't always need a custom hook.&lt;/p&gt;

&lt;p&gt;As I've already mentioned, using custom React Hooks is great for using state across multiple components. If you need to keep track of certain data and it's scoped to only one component and maybe any children and doesn't need to be accessed anywhere else, &lt;code&gt;useState&lt;/code&gt; within that particular component is acceptable. Don't make things any more complicated than they need to be.&lt;/p&gt;

&lt;p&gt;Using my own hooks has changed the way my code looks and operates and it makes me a better developer!&lt;/p&gt;

&lt;p&gt;If you would like to read more about custom hooks, check out &lt;a href="https://reactjs.org/docs/hooks-custom.html"&gt;React's official documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>reacthooks</category>
    </item>
    <item>
      <title>Why I use React Query</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Sun, 05 Sep 2021 14:18:28 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/why-i-use-react-query-97k</link>
      <guid>https://forem.com/imtyrelchambers/why-i-use-react-query-97k</guid>
      <description>&lt;p&gt;I tend to write my APIs as REST. However, when I implemented GraphQL in one of my projects, I loved the way it provided its fetching status, data and other information.&lt;/p&gt;

&lt;p&gt;Being able to control your UI via variables such as &lt;code&gt;isLoading&lt;/code&gt;, was a lot of fun.&lt;/p&gt;

&lt;p&gt;I used to try and write my own solid way to fetch data from my APIs but controlling my UI becomes a chore. For example, not loading a page before certain data came in used to be more hit or miss than anything else.&lt;/p&gt;

&lt;p&gt;I'd heard of React Query, but I wasn't exactly sure what it did and why I should use it. Like any new project, I decided I'd give it a shot because I like to incorporate something new in every new project. What I was left with was an amazing experience.&lt;/p&gt;

&lt;p&gt;It behaves similarly to GQL in the sense that you have access to a bunch of variables that give you finer control over your calls and therefore, over your UI. &lt;strong&gt;Not only that&lt;/strong&gt;, but it can also replace your global state management.&lt;/p&gt;

&lt;p&gt;I'm by no means an expert on React Query as I've just started using it myself, but maybe I can help you make the decision to give it a try.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it can replace global state management
&lt;/h2&gt;

&lt;p&gt;This confused me greatly when I first began using React Query. I wasn't sure how I could get a user, for example, save the data, and use it somewhere else without making a needless call.&lt;/p&gt;

&lt;p&gt;When React Query makes a call, the data is cached (this is your "state"). This means that instead of making another call, it will serve up the data it has in its cache and give it to you. When that becomes stale, it will fetch from the API and the process repeats. You can also specify how long data should stay fresh (stay in the cache without being fetched) and you can also invalid other queries, but let's not get ahead of ourselves.&lt;/p&gt;

&lt;p&gt;In one of my projects, I replaced 90% of my global state with the help of React Query.&lt;/p&gt;

&lt;h2&gt;
  
  
  How React Query is used
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;This may or may not be correct so please don't call me trash on the internet or I'll tell my mom.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I think in order to effectively communicate what I'm thinking, I like to use pictures so I can walk you through my code.&lt;/p&gt;

&lt;p&gt;I'll give you the full snippet here, then break it down piece-by-piece to try and explain it better.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// hooks/mutations/useLogin.js
export const useLogin = () =&amp;gt; {
  const history = useHistory();
  const queryClient = useQueryClient();
  const { mutate } = useMutation((data) =&amp;gt; login(data), {
    onSuccess: (res) =&amp;gt; {
      queryClient.invalidateQueries("currentUser");
      const { token } = res.data;

      window.localStorage.setItem("token", token);
      history.push("/dashboard");
    },
  });

  return mutate;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're familiar with GraphQL, then this will look a little familiar to you and it's what I loved about React Query.&lt;/p&gt;

&lt;p&gt;I encourage you not to copy this code if you're learning it for the first time but type it out yourself so you burn that into your memory banks.&lt;/p&gt;

&lt;p&gt;Our useMutation function takes in whatever data we pass to it. If you were to look at my LoginForm, you would see a line of code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const login = useLogin()
  login({firstName: "tyrel", lastName: "chambers"})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whatever data you pass to your "login" function (it can be whatever name you want it to be), will get passed as an argument in our useMutation function in our hook. The &lt;code&gt;login(data)&lt;/code&gt; piece you see there, is the API call we make with the data we passed in from the actual login form. In our case, it's the email and password of the user. Now we can send this to the server to be validated and have our user returned.&lt;/p&gt;

&lt;p&gt;In my case, I have a function called &lt;strong&gt;login&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// api/login.js
export const login = ({ email, password } = {}) =&amp;gt; {
  return request.get("/login", {
    params: {
      email,
      password,
    },
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just for the sake of clarity, I've included the function that makes the actual API call.&lt;/p&gt;

&lt;h3&gt;
  
  
  We have our user, now what?
&lt;/h3&gt;

&lt;p&gt;Within React Query's mutation and query functions, we have access to the property &lt;code&gt;onSuccess&lt;/code&gt;. This does what you think it would do, when the query or mutation finishes in a successful state, execute &lt;em&gt;this&lt;/em&gt; code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  onSuccess: (res) =&amp;gt; {
    queryClient.invalidateQueries("currentUser");
    const { token } = res.data;

    window.localStorage.setItem("token", token);
    history.push("/dashboard");
  },

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my &lt;code&gt;onSuccess&lt;/code&gt; block, I invalidate my initial user query.&lt;/p&gt;

&lt;p&gt;You can name queries, you see. This is so if you need to invalidate some data (which means to force React Query to fetch it again from an API). The beauty about this is you can use invalidating queries in tandem with your cache (and stale times) to create an effective system for your API calls. When I invalidate my &lt;code&gt;currentUser&lt;/code&gt; query, this means that React Query will make a call to the database to fetch my authorized user instead of using the cached version. This is also helpful if you update your user's profile or account in your web app. You can invalidate that query which would force React Query to fetch from the API again, which returns and caches an up-to-date user object. It's pretty wicked.&lt;/p&gt;

&lt;p&gt;The rest of the block is saving the JWT I receive and redirecting to the dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing React Query's cache
&lt;/h2&gt;

&lt;p&gt;React Query really makes working with API and data, fun. Rather than setting up stores to save my current user (or really anything else), we can use React Query's caching ability. That's where I got a little confused on how to access that data.&lt;/p&gt;

&lt;p&gt;I'll provide another quick example of how you can use React Query's caching ability as your data store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const useUser = () =&amp;gt; {
  const token = window.localStorage.getItem("token");
  const info = useQuery("currentUser", getUser, {
    enabled: !!token,
    staleTime: 1000 * 60 * 5,
  });

  return { ...info, user: info.data };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is my hook that queries my database for the current authorized user. I supply my JWT and off it goes. When it comes back, I set a longer stale time because it's unlikely the user object will change and when it does change, I'll invalidate it anyway and force React Query to fetch an updated user object.&lt;/p&gt;

&lt;p&gt;Here is how it is used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const { isLoading, user } = useUser();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the function &lt;code&gt;useUser()&lt;/code&gt; is called, it'll do one of two things. First, it'll check to see if this data in its cache is fresh (the data being my user object) and if it isn't it'll do the second action which is fetching the user from the database. So it'll either use the user in cache or it'll fetch a new one.&lt;/p&gt;

&lt;p&gt;I thought that if I included &lt;code&gt;useUser()&lt;/code&gt; that it would call the API regardless so I wasn't sure how to use React Query to avoid that. What I found out is that all I have to do is execute that function call and let React Query do the rest. It'll check its cache and determine whether or not to make a call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;React Query has been an absolute delight to work with. Going from an unstructured, unopinionated way of making API calls and controlling the flow of information to my UI, to one that gives me finer control over how things flow, makes my code feel more stable. I can count on the data I need, to be there and with the variable they expose, I can control the state of my UI by hiding pages or waiting on data to arrive before proceeding in a more effective manner.&lt;/p&gt;

&lt;p&gt;I would 100% recommend you take the time to learn this technology if you're looking for a fun way to make API calls :)&lt;/p&gt;

&lt;p&gt;And that ladies and gentlemen, is Why I use React Query.&lt;/p&gt;

</description>
      <category>api</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>When to use Flex vs. Grid</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Sat, 04 Sep 2021 23:23:04 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/when-to-use-flex-vs-grid-1ef5</link>
      <guid>https://forem.com/imtyrelchambers/when-to-use-flex-vs-grid-1ef5</guid>
      <description>&lt;p&gt;Confusion around Flex and Grid is still prevalent in today's developmental world. It's easy to miss the difference between the two since, on a basic level, they do the same thing. They help you layout your page in a structured way.&lt;/p&gt;

&lt;p&gt;It took me a while to see the difference between them and why I should use Grid in some circumstances and Flex in the others. Hopefully, I can help you decide when to use Grid and when to use Flex.&lt;/p&gt;

&lt;p&gt;This won't be a tutorial like you'd expect- there won't be any code. This article will be more of a high-level explanation on why/when we use Grid and why/when we use Flex. I know there are a ton of posts and videos out there talking about this particular comparison, but I'm hoping that maybe I can help you understand the difference.&lt;/p&gt;

&lt;p&gt;Grid was designed for 2-dimensional layouts (rows and columns at the same time). Flex was designed for 1-directional layouts (rows or columns). These two methods share similar properties and behaviours, but both have their own use-cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grid
&lt;/h2&gt;

&lt;p&gt;Grid is amazing for creating a predictable layout on a macro level where the structure is important. It makes it easy to work with different screen sizes without worrying about throwing your page out of whack. Sometimes Flex can act a little unpredictably on a grand scale and if you've worked with Flex, you know what I mean.&lt;/p&gt;

&lt;p&gt;In the example below, there is a list of items, of which I've only shown four, that shows a Grid layout in action.&lt;/p&gt;

&lt;p&gt;Keep in mind that in reality, I could use a Flex layout to achieve this Grid look. However, when using Grid, it gives me more control over the width of the items, how they wrap around and flow down the page, and makes my UI a lot more stable.&lt;/p&gt;

&lt;p&gt;This example comes from &lt;a href="https://reddex.app" rel="noopener noreferrer"&gt;Reddex&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20reus2dnto2zobc65rq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20reus2dnto2zobc65rq.png" alt="Grid example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each item is part of a Grid. It's currently 2-wide on larger screens but shrinks to a 1-wide grid.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3naxwt6xyrkizk8sbq4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3naxwt6xyrkizk8sbq4.jpg" alt="Grid example 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If there are multiple items to be shown on a page and structure matters, I tend to use Grid. I know the graphic isn't the most high-tech or advanced, but using it in the way that is shown, is how I tend to use it. It's also used because we are working with a multi-dimensional layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flex
&lt;/h2&gt;

&lt;p&gt;Flex is great for working with individual items. It allows you to work the whitespace around your items to your advantage. The size of each Flex item will determine how much size it takes up in its parent container. Using certain properties, you can decide how they interact with their siblings.&lt;/p&gt;

&lt;p&gt;On the other hand, Flex is great for layouts on a micro-level. I use Flex religiously on a smaller scale like with the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcx1n4bhhx2adv9e1tftv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcx1n4bhhx2adv9e1tftv.png" alt="Flex example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The parent of each item is &lt;code&gt;display: Flex&lt;/code&gt; (oops, I included some code). This lays out my content on the X-axis, in a row. When the parent gets too small to fit them on the same axis, they will wrap below each other.&lt;/p&gt;

&lt;p&gt;Technically, I could use Grid to accomplish the same task, but it would add a little more work and a little more structure. Because that content is only on 1-axis, using Flex makes more sense.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk74kew2qip73csp3v7j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk74kew2qip73csp3v7j.jpg" alt="Flex example 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, I use Flex on a small scale where the structure of these items isn't entirely important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Grid is used to layout content on a multi-dimensional level whereas Flex is only used in 1-dimension.&lt;/p&gt;

&lt;p&gt;If you need flexibility, use Flex. If you need structure and predictability, use Grid.&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>What is a Ternary Operator in JavaScript?</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Sat, 04 Sep 2021 22:49:45 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/what-is-a-ternary-operator-in-javascript-53bg</link>
      <guid>https://forem.com/imtyrelchambers/what-is-a-ternary-operator-in-javascript-53bg</guid>
      <description>&lt;p&gt;A ternary operator is a way to write if/else statements in one line, however if you've never heard of them before, they can be tricky to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of a Ternary Operator
&lt;/h2&gt;

&lt;p&gt;They consist of 3 fundamental components.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A condition followed by a question mark (?)&lt;/li&gt;
&lt;li&gt;An expression to execute if the condition is truthy&lt;/li&gt;
&lt;li&gt;An expression to execute if the condition is falsy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A ternary operator is written like this: &lt;code&gt;isTrue ? true : false&lt;/code&gt;. We have our condition &lt;code&gt;isTrue&lt;/code&gt;. It's the variable we are checking. It represents &lt;code&gt;if (isTrue)&lt;/code&gt; in a traditional if statement.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;true&lt;/code&gt; after the question mark represents tha expression we will execute if the variable we are checking returns a truthy value.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;false&lt;/code&gt; is the expression we will execute if the variable is falsy.&lt;/p&gt;

&lt;p&gt;It took me a while to figure out how to actually write ternary operators because I didn't understand them, so I stuck with if statements until I forced myself to learn them.&lt;/p&gt;

&lt;p&gt;Since then, I use them quite often, however ternary operators shouldn't always be used, in my opinion.&lt;/p&gt;

&lt;p&gt;I use ternary operators when I need a quick check if something is true or false. Use whatever gets the job done, but also provides decent readability.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it differs from an if statement
&lt;/h2&gt;

&lt;p&gt;Ternary operators work more like if/else statements.&lt;/p&gt;

&lt;p&gt;If I were to write...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;isTrue ? true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would be like writing...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (isTrue) {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is our ternary operator (the first code block) will fail whereas our if statement will succeed.&lt;/p&gt;

&lt;p&gt;It needs an expression to execute for a truthy or falsy result. With that said, ternary operators are more like if/else statements.&lt;/p&gt;

&lt;p&gt;They really work liike...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (isTrue) {
  truthy()
} else {
  falsy()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A complete example
&lt;/h2&gt;

&lt;p&gt;In order to wrap this up with a pretty bow, I'll include a full example of how a ternary operator can be used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const favouriteColour = "blue"

const isBlue = favouriteColour === "blue" ? "it's true! it's blue!" : "it's not blue :("

console.log(isBlue) // "it's true! It's blue!"

// Let's change our fav colour

const favouriteColour = "red"

const isBlue = favouriteColour === "blue" ? "it's true! it's blue!" : "it's not blue :("

console.log(isBlue) // "it's not blue :("
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ternary operators are amazing to use. If you're having a tough time understanding it, just keep practicing and working at it. As with anything that is unknown, through consistent practice, you'll understand it before you know it.&lt;/p&gt;

&lt;p&gt;Just remember &lt;code&gt;favouriteColour === "blue" ?&lt;/code&gt; is our condition, &lt;code&gt;? truthy :&lt;/code&gt; is our code to execute if it's truthy, and &lt;code&gt;: falsy;&lt;/code&gt; is our code to execute if it's not.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>I Built a Developer Productivity Tool!</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Wed, 07 Apr 2021 00:28:04 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/i-built-a-developer-productivity-tool-3m24</link>
      <guid>https://forem.com/imtyrelchambers/i-built-a-developer-productivity-tool-3m24</guid>
      <description>&lt;p&gt;Hey everyone!&lt;/p&gt;

&lt;p&gt;After many failed interviews absolutely nuked my self-confidence and love for programming, I decided I didn't really like the idea of working for someone else on their product. It's not that I never would, but I instead decided to focus on making my own ideas. &lt;/p&gt;

&lt;p&gt;For the past handful of months I've been working on this little thing called &lt;a href="https://kanlen.ca"&gt;Kanlen&lt;/a&gt;. I'm still figuring out exactly what "it is".&lt;/p&gt;

&lt;p&gt;I'm not a fan of wasting too much time on repetitive tasks even though I repeat myself often, but, here we are.&lt;/p&gt;

&lt;p&gt;I had an idea one day. What if there was a way, to build our your CLI command using a GUI of sorts? That would take the guesswork out of figuring out the correct syntax for the CLIs you use. For me, it's a lot of CRA.&lt;/p&gt;

&lt;p&gt;It might also be nice to know what flags I have available to use in this command. I sure can't remember all that.&lt;/p&gt;

&lt;p&gt;I'm going to briefly explain what Kanlen does and maybe from that, an idea of the app will form, but for now let's think of it like a developer's productivity tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;Well, we already spoke about the whole CLI thing, so on we go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Save Environment Variables
&lt;/h3&gt;

&lt;p&gt;If you're like me, sometimes you get a new development computer and forget to jot down the environment variables you're using. That happened to me while building Reddex. Losing those variables was super annoying.&lt;/p&gt;

&lt;p&gt;I thought it might be cool if you could store them somewhere online. Then if you switch machines, it crashes, or spontaneously combusts, you'll have a backup of those variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Save your code like Gists
&lt;/h3&gt;

&lt;p&gt;Perhaps the bread and butter of Kanlen, well, at least the main idea of it since the beginning, was being able to save your favourite functions.&lt;/p&gt;

&lt;p&gt;I have a few pieces of code that I like to bring between projects and if I could save them in a spot that's easily accessible, obviously that would be pretty alright.&lt;/p&gt;

&lt;p&gt;These pieces of code are sharable and if you download the Kanlen VSCode extension, you can import your saved snippets right into your editor. On the flip-side, if you want to create a snippet, you can do that by selecting a block of code and running a specific command. It's pretty neat in my humble opinion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving on
&lt;/h2&gt;

&lt;p&gt;Those are a couple things it does, it you're interested in reading more about it, check out the main page &lt;a href="https://kanlen.ca"&gt;Kanlen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is just a little side project I've been playing with since late 2020. It's still in development, but I love where it's going and maybe it'll help you too someday.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>sideprojects</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Be a Better Self-learner</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Mon, 12 Aug 2019 16:44:56 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/be-a-better-self-learner-1mlj</link>
      <guid>https://forem.com/imtyrelchambers/be-a-better-self-learner-1mlj</guid>
      <description>&lt;p&gt;I want to preface this by saying I think formal education is great, and required for certain fields. For example, you're not going to become a brain surgeon by watching YouTube. I think it's fairly obvious what can be self-taught vs what you should go to school for. I'm a web developer of which I'm self-taught. Going to school at this point would not only be redundant, but it would be a waste of money.&lt;/p&gt;

&lt;p&gt;For being a web developer, you &lt;em&gt;can&lt;/em&gt; go to school and get a degree in it which will help you in the long run in terms of giving the company you're interviewing with, confidence in what you can do. However, this isn't for everyone.&lt;br&gt;
I have a diploma from the local college in Law &amp;amp; Security, so I have a background in a formal education. Now, college was great, the structure was pleasing, networking with professor's and classmates, was a definite plus, however, one thing I've learned with being a self-taught web developer is: learning how to learn.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch tutorials &amp;amp; read blogs
&lt;/h2&gt;

&lt;p&gt;The best way to get started learning something new, is to watch tutorials on getting started in the field you'd like to get into. Youtubing or reading blog posts on getting started in whatever skill you're looking to learn, will help you plan out your path to success. It'll help you figure out the skills you may need to learn along the way.&lt;/p&gt;

&lt;p&gt;I started out by Youtubing: how to create a website from scratch. My first entry into creating static websites was using notepad and saving an index.html file to my desktop. Everyone starts somewhere, but it's important to figure out where to start. Watching tutorials (many tutorials) will help you figure that out.&lt;br&gt;
It would also be beneficial to look at websites like SkillShare or Udemy, even PluralSight for courses developed by professionals (if you'd like everything all in one place, otherwise stick to Youtube).&lt;/p&gt;

&lt;h2&gt;
  
  
  Mentally create your dream project
&lt;/h2&gt;

&lt;p&gt;Tutorials are great and you'll be able to create little projects here and there if they're present in the blog you're currently reading, or the video you're currently watching.&lt;/p&gt;

&lt;p&gt;However, creating your own project is equally as important, but first you have to dream it up. Dreaming is critical to learning, in my humble opinion. If you're not dreaming, you're likely not being inspired. It's ok to dream, no matter what others try to tell you. Dreaming creates hope, a plan of action, motivation.&lt;br&gt;
Dreaming of your next big app can help kickstart your journey to creating that app. It'll help you with critical thinking and planning out your roadmap to see you from start to finish.&lt;/p&gt;

&lt;p&gt;Dreaming however, is hard! Not many people can or know how, or don't let themselves dream. How do I dream? Usually, like everyone, it's in the shower or on the toilet - let's be real. Thoughts will pass through my head around the lines of "How cool would it be if this", or, "How cool would it be if that". Once you latch onto an idea that sparks some interest, run with it. Think about how it might be done, what would it accomplish?&lt;br&gt;
Coming up with these ideas usually revolves around dreaming of solving a problem. That problem may be an issue that only affects you, or it may affect a few people. If you're interested, I wrote a &lt;a href="https://dev.to/imtyrelchambers/build-a-reddit-scraper-problem-solution-337k"&gt;series of blog posts&lt;/a&gt; about solving a problem that I had, that also has been solving a problem for others.&lt;/p&gt;

&lt;p&gt;While dreaming is great, creating your own personal projects in the beginning, is wildly important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create personal projects
&lt;/h2&gt;

&lt;p&gt;In the beginning it's important to create personal projects that may never amount to much. The point isn't creating to project, but it's learning the many techniques along the way. Having a goal (creating the project) inspires action toward that goal. This results in learning how to create an index file, adding elements to that page, learning how to style elements, creating links etc. Because there's a goal, there's a clear plan of action, or atleast the ability and environment for such a plan to be created.&lt;/p&gt;

&lt;p&gt;The most popular project people create while learning how to program is a todo list. It's the Hello World of personal projects. It's simple to start and covers HTML, CSS, and Javascript. Creating such a tool will help familiarize yourself with different aspects of those languages and general app development.&lt;br&gt;
Personal projects should be quick throwaways that will help you learn the basics. Now, just do that a bunch of times! But, no, not really... actually yes, do it a lot. Do it until you feel comfortable solving a problem. Who knows, maybe you'll solve a problem later on using a "throwaway" you created before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get involved in open source
&lt;/h2&gt;

&lt;p&gt;While I don't personally contribute to open source software (OSS), there is definitely a benefit to doing so. It'll get you used to working with a team of people, solving issues from simple bugs, to complex problems or refactoring code.&lt;br&gt;
Once you become comfortable with HTML, CSS and JS, try looking through GitHub for good first-issues.&lt;/p&gt;

&lt;p&gt;Working on OSS also looks pretty great on a resume.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does this apply to other hobbies?
&lt;/h2&gt;

&lt;p&gt;It'll be different for everyone, but I'm hoping that what I laid out will help you extrapolate and apply certain aspects of the journey I took to becoming a web developer.&lt;/p&gt;

&lt;p&gt;Learning an instrument?&lt;/p&gt;

&lt;p&gt;Practice 3-4 major chords, get efficient at chord changes to and from, mixing up the order in which you transition. Then practice a popular song that uses those chords and maybe one more that you don't know. That way you can have fun learning something you know, and it'll feel pretty good too.&lt;/p&gt;

&lt;p&gt;Want to learn to draw?&lt;/p&gt;

&lt;p&gt;Start out learning basic techniques and shapes. Sketching, the fundamentals of drawing. Once again, go to Youtube and watch some tutorials on drawing, then apply those immediately after, and practice them until you're good. Once you get started, you'll begin to figure out how to progress.&lt;/p&gt;

&lt;p&gt;The point of being an effective self-learner, is to always learn. Push your boundaries and look for opportunities to stretch your comfort zone. If you stay stagnant and in one realm, you'll remain there. Growth and exploration are the foundation of self-learning.&lt;/p&gt;

&lt;p&gt;So figure out what you want to do, figure out how to start, then get going and have fun along the way.&lt;/p&gt;

</description>
      <category>growth</category>
      <category>learning</category>
      <category>education</category>
    </item>
    <item>
      <title>Build a Reddit Scraper: Setting up Dexie.js</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Sun, 21 Jul 2019 00:48:58 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/build-a-reddit-scraper-setting-up-dexie-js-19ci</link>
      <guid>https://forem.com/imtyrelchambers/build-a-reddit-scraper-setting-up-dexie-js-19ci</guid>
      <description>&lt;p&gt;Welcome! Today I want to walk you through the basics of setting up Dexie.js.&lt;/p&gt;

&lt;p&gt;What Dexie is, is a wrapper around IndexedDb. It allows you to save more data than localStorage or sessionStorage could (they hold up to 5mb of data each). If you need to go above that, you can use IndexedDB. In most situations, I wouldn't really recommend it, especially if it's data that needs to persist. However for this scraper, whether the posts we pulled will get wiped or not, doesn't matter, we can just pull them again.&lt;/p&gt;

&lt;p&gt;If you'd like to read up more on IndexedDb or Dexie, go to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API"&gt;https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API&lt;/a&gt; and &lt;a href="https://dexie.org/"&gt;https://dexie.org/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Dexie is actually fun to use, simple to setup, and their docs are developer friendly. They give clear examples and how-tos, which I appreciated&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Setting up Dexie.js&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I'm going to assume you have Dexie installed now. If you don't, go to the link above and download the library.&lt;/p&gt;

&lt;p&gt;Thankfully, it's pretty quick to set up and they give you a basic example of using Dexie right on the front page.&lt;/p&gt;

&lt;p&gt;For our app, I created a &lt;code&gt;Database.js&lt;/code&gt; file where I hold the configuration and instantiation of our database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Dexie&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dexie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Dexie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reddex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, author, title, selftext, ups, url, num_comments, created&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, author, title, selftext, ups, url, num_comments, created&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subreddits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, subreddit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, author, title, selftext, ups, url, num_comments, created, flair&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subreddits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, subreddit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, author, title, selftext, ups, url, num_comments, created, flair, postId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;authors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subreddits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id, subreddit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's my entire file.&lt;/p&gt;

&lt;p&gt;To make use of this, we need to import it &lt;code&gt;db&lt;/code&gt;. I used React for this project so I imported it into my app.js file &lt;code&gt;import db from 'path/to/file'&lt;/code&gt;. If you're using straight vanilla JS, you could do the same thing, just import it into the top of your main scripts file.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const db = new Dexie("Reddex");&lt;/code&gt; will open the current database (that means it'll use the current database 'Reddex') if it exists, if it doesn't exist, it will create it. I like that.&lt;/p&gt;

&lt;p&gt;Each key represents a table in the database, where the comma-separated values represent the columns. The &lt;code&gt;++id&lt;/code&gt; will add &lt;code&gt;1&lt;/code&gt; to the prior index so each entry has its own id that continues incrementing. The names of the columns are what will give us access to those values.&lt;/p&gt;

&lt;p&gt;So, we import Dexie and create our new database. Inside the Dexie params, will be the name of the database that you want to create. Since my app is called Reddex (&lt;a href="https://reddex.app"&gt;https://reddex.app&lt;/a&gt;), that will be the name of my database.&lt;/p&gt;

&lt;p&gt;It's best practice to create a new version for every change to the database model schemas. Every time I need to add something to my database, I create a new version. The one thing I haven't done now, which I'll get to, is upgrading previous versions of the database, to the newer version. &lt;/p&gt;

&lt;p&gt;I've already run into database versioning issues which I believe will be solved by upgrading them. From the docs, here is an example of performing an upgrade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Dexie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FriendsAndPetsDatabase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id,name,birthdate,sex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id,name,kind&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;upgrade&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;YEAR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;365&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toCollection&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;modify&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;friend&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;friend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;birthdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;friend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;YEAR&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;friend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Always keep the declarations previous versions&lt;/span&gt;
&lt;span class="c1"&gt;// as long as there might be users having them running.&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;stores&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;friends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id,name,age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;++id,name,kind&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Saving To Our Dexie Database&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we have set up our database, we're ready to go ahead and begin saving data to it.&lt;/p&gt;

&lt;p&gt;As I briefly touched on in a previous post (&lt;a href="https://dev.to/imtyrelchambers/build-a-reddit-scraper-fetching-posts-4m50"&gt;https://dev.to/imtyrelchambers/build-a-reddit-scraper-fetching-posts-4m50&lt;/a&gt;), I gave a brief overview of this saving process, which I'll touch on again here for the sake of keeping like concepts together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveToDatabase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; 
  &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;newPosts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;newPosts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;selftext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selftext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;num_comments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;num_comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;flair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;link_flair_text&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to access our database from anywhere, the solution I went with was creating a variable and attaching it to the window's scope. I'm sure there's a better way to do this.&lt;/p&gt;

&lt;p&gt;To create this variable, it goes something like this &lt;code&gt;window.db = db;&lt;/code&gt;. Then we can access it from everywhere in our app. &lt;/p&gt;

&lt;p&gt;You'll notice we access the window scope again to grab our &lt;code&gt;db&lt;/code&gt; variable. Because we have a table called &lt;code&gt;posts&lt;/code&gt;, that's what we chain onto our database variable. It says: I want to access our database and &lt;code&gt;add()&lt;/code&gt; to our &lt;code&gt;posts&lt;/code&gt; collection (or table depending on the terminology you're used to).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Grabbing Data From Our Database&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we've saved our data, we need a way to grab it. Below I created a function that will do just that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPostsFromDatabase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setPosts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;setPosts&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like saving to our database, we want to grab the data contained in our &lt;code&gt;posts&lt;/code&gt; collection (I'm calling it collection). We have to turn that data into an array so we can use it like normal later on.&lt;/p&gt;

&lt;p&gt;In order to do this, we need our famous global variable which we will use to access our collection once again. To grab the data, we need to access the collection we want to pull from, in our case, it's &lt;code&gt;posts&lt;/code&gt;, then we need to chain &lt;code&gt;toArray()&lt;/code&gt; onto it. There, we're done. The next step would be to set whatever state or variable with the data we have.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Clearing Records From The Database&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For Reddex, I wipe the database before I write to it. This is because I don't want duplicate entries saved and being retrieved later on. It makes sure that the data being displayed, is the same as the data being saved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deletePostsCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I was a good developer I'd probably do something in the &lt;code&gt;then()&lt;/code&gt; and &lt;code&gt;catch()&lt;/code&gt; portions of the promise being return, but here we are! (I joke of course (about myself)). This is where you'd display any errors that mean anything to the user or you want to do some random thing once the promise resolves, it's up to you.&lt;/p&gt;

&lt;p&gt;That's about it when it comes to using Dexie.js! Give it a shot! Thank you for making it this far.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FIN&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>dexie</category>
      <category>react</category>
      <category>javascript</category>
      <category>reddit</category>
    </item>
    <item>
      <title>Build a Reddit Scraper: Composing Messages</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Thu, 18 Jul 2019 15:56:33 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/build-a-reddit-scraper-composing-messages-4ilp</link>
      <guid>https://forem.com/imtyrelchambers/build-a-reddit-scraper-composing-messages-4ilp</guid>
      <description>&lt;p&gt;Today let's talk about composing and sending messages via the Reddit API using JavaScript and React (is that good SEO?).&lt;/p&gt;

&lt;p&gt;This requires that the user has said "ok" to you using their account, in fact, the majority of what I'm talking about is based off that.&lt;/p&gt;

&lt;p&gt;So let's assume they said "ok" and you've set up your form and all we need to do is access that info and send a message with the API.&lt;/p&gt;

&lt;p&gt;The context of this post will be in our confirm message component. This is the part where we have selected our posts and are ready to send our messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Creating Our Submit Handler&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The link we will be making our post call to, is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://oauth.reddit.com/api/compose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we originally authenticated using the OAuth method, we need to make calls to the OAuth subdomain endpoint &lt;code&gt;api/compose&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The tricky part was that you can't just send a post body request like normal, but instead we have to create some FormData. This is the way I ended up getting it to work, maybe there's a better way!&lt;/p&gt;

&lt;p&gt;Let's pick apart the function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendMessageToAuthors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;removeMessagedAuthor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchTokens&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fmtSubject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://oauth.reddit.com/api/compose`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Something went wrong&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A messaged is needed to send&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fmtSubject&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A subject is needed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason why I stored my tokens for Reddit in the database was because I can now fetch them whenever and wherever I need them. I thought what might happen if local storage got cleared, or this or that. Atleast I know these tokens are always up to date because whenever the app is loaded, the app takes the refresh_token in the database, asks Reddit for a new access_token, then saves it to the user by passing the JWT token saved in localstorage, to the database.&lt;/p&gt;

&lt;p&gt;So, we call that function to get our tokens which we will use to handshake with Reddit, which is to say, we will use it to show Reddit we are authenticated.&lt;/p&gt;

&lt;p&gt;Because the ability to send messages is a power that requires great responsibility, the checks have to be on point. If anything is missing, we return, if anything is falsey, we return. This will prevent situations where perhaps we send a message and the title is &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Formatting Our FormData&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The next portion of the function, which will be added below our validation checks, is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`/u/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fmtSubject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Message sent to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;removeMessagedAuthor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If any of our variables are empty, we immediately return and run away.&lt;/p&gt;

&lt;p&gt;Otherwise, we start setting our form data. Your username is technically a "subreddit" according to the Reddit API, I was confused by that. That's why we need &lt;code&gt;/u/&lt;/code&gt; before our actual author's name. I'm assuming it stands for a user's endpoint, but is referred to as a subreddit in the docs.&lt;/p&gt;

&lt;p&gt;Instead of doing the encryption we did before, in the original request to get our tokens, we instead include our access_token as the bearer token. The &lt;code&gt;x-www-form-urlencoded&lt;/code&gt; portion is also very important.&lt;/p&gt;

&lt;p&gt;Why you ask? Because it sends our &lt;code&gt;author&lt;/code&gt;, &lt;code&gt;subject&lt;/code&gt;, and &lt;code&gt;message&lt;/code&gt; as key/value pairs separated by &lt;code&gt;&amp;amp;&lt;/code&gt; for each pair, and assigned using &lt;code&gt;=&lt;/code&gt;. More info on MDN: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST"&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's just another way to tell the server what type of data we are sending; what the structure is.&lt;/p&gt;

&lt;p&gt;The toast is just sending a notification and removing the author is just a personal touch from me. What it does, is it automatically removes the author you sent a message to, from the list of total selected messages. This is useful for rapidly sending messages. Whereas before you'd send a message, click next, send, next etc., now it'll automatically go to the next one ultimately by resetting the index to 0 (more on that later).&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Save Our Author to the Database&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Just in case you're interested. For our Reddit scraper in this particular use case, I save the author to the database because our app will automatically choose between two different saved messages that I'll show you about once we get to the account page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveAuthorToDb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REACT_APP_BACKEND&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/profile/saveAuthors`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;postId&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;token&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We send the name of the author to our backend and save it. Then, when we retrieve it, we can cross reference with the author of the currently displayed author and go from there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messageHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;authorExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorsMessaged&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;authorExists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;authorExists&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;setDefaultMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;altMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;setDefaultMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Handling Our Component State&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;defaultMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDefaultMessage&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSubject&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;redditProfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setRedditProfile&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setSubject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reddit_profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nx"&gt;setRedditProfile&lt;/span&gt;&lt;span class="p"&gt;({...&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;messageHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason why I did the state this way, is because if you need to make an adjustment to the current title, or message body, you can, and it won't affect the next post. It becomes prefilled with the message you've created, or, you can add you're own.&lt;/p&gt;

&lt;p&gt;The useEffect's are just updating our state when the app mounts and when either data or data.title changes (you can reference the messageHandler function in the prior snippet).&lt;/p&gt;

&lt;p&gt;That's about it! Just build out the UI as you'd like, and this info will helpfully get you going with sending messages! &lt;/p&gt;

&lt;p&gt;At the time of writing, I don't have a loader on the button. I'd recommend this. When someone sends a messages, a loader should replace the button. This, to me, is just good UX and by getting rid of the button, it can prevent spamming.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this post, now go make yourself an awesome Reddit scraper using JavaScript and React (more SEO, I think).&lt;/p&gt;

&lt;p&gt;Bye!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>reddit</category>
      <category>api</category>
    </item>
    <item>
      <title>Build a Reddit Scraper: Authenticating With Reddit OAuth</title>
      <dc:creator>Tyrel Chambers</dc:creator>
      <pubDate>Mon, 15 Jul 2019 16:13:35 +0000</pubDate>
      <link>https://forem.com/imtyrelchambers/build-a-reddit-scraper-authenticating-with-reddit-oauth-4519</link>
      <guid>https://forem.com/imtyrelchambers/build-a-reddit-scraper-authenticating-with-reddit-oauth-4519</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;Create Your Reddit Developer App&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;We’re continuing our series on Build a Reddit Scraper. This will be done using JavaScript!&lt;/p&gt;

&lt;p&gt;To get started with authenticating with the Reddit API, we need to create a developer app that will allow us to actually use the more advanced features of Reddit’s API.&lt;/p&gt;

&lt;p&gt;For reference, you can find everything I’m talking about, here: &lt;a href="https://github.com/reddit-archive/reddit/wiki/oauth2"&gt;https://github.com/reddit-archive/reddit/wiki/oauth2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ll walk you through what I did to create a pretty stable system in my eyes. I’ve done a bunch of different tests - but that’s subjective.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Finding The Right Scope&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Alright, the first thing we need to do is direct the user to a special endpoint that will present them with a choice to either give our app permission to authenticate with their account, or not.&lt;/p&gt;

&lt;p&gt;You’ll have to fill out the certain portions yourself that call for your app name and secret. You can find these values on the page where you created your Reddit app on their website.&lt;/p&gt;

&lt;p&gt;My link looks like this -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const link = `https://www.reddit.com/api/v1/authorize?client_id=${process.env.REACT_APP_REDDIT_APP_NAME}&amp;amp;response_type=code&amp;amp;state=RANDOM_STRING&amp;amp;redirect_uri=${process.env.REACT_APP_REDDIT_REDIRECT}/signup&amp;amp;duration=permanent&amp;amp;scope=privatemessages identity`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documentation does a pretty good job of explaining each individual requirement here, but one thing that tripped me up was try to interpret the actual documentation when it comes to figuring out what scopes you need.&lt;/p&gt;

&lt;p&gt;The scope is: what parts of a user’s profile will you need access to? As you can see, with my app, I want to be able to see get the user’s profile itself, so I chose Identity, and I need to be able to send/receive private messages.&lt;/p&gt;

&lt;p&gt;If you go to &lt;a href="https://reddit.com/dev/api"&gt;https://reddit.com/dev/api&lt;/a&gt; you can see a list of documentation. It’s pretty overwhelming at first and there isn’t a whole lot to help guide you through it. Hopefully I can help with that.&lt;/p&gt;

&lt;p&gt;In the left sidebar, it shows you the endpoints that you can access. Look for what you think you’ll need in terms of access, and at the top of that particular section, you’ll find the scope. This scope is what you will need to add into the scope section of the url above. &lt;br&gt;
If you need more than one scope, make sure to add a space between each scope item.&lt;/p&gt;

&lt;p&gt;Beside each subtitle in the main column of content on the right, you can also find the REST action that represents that endpoint, and the scope that will be needed to access it (the green rectangle).&lt;/p&gt;

&lt;p&gt;So now we have access to our scopes. You can throw that link into a function that gets called on click or something, and have the browser open a new tab, or just change the url in the current tab. The user will be taken to Reddit where they will then allow or deny your app. &lt;/p&gt;

&lt;p&gt;If they approve it or deny it, they will be redirected back to your app, to the URL specified when you first created the Reddit app. The redirect uri is the URL that Reddit will redirect to after they have made a decision, so make sure you’re redirecting back to the page that is handling your signup process.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Getting The Access Token&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now the user has hypothetically said “yes” to our app’s request.&lt;/p&gt;

&lt;p&gt;We now have to make a post request to another endpoint. But before we can do that, we need to parse the URL query parameters. &lt;/p&gt;

&lt;p&gt;When the user is redirected back, the URL includes a “code” parameter that we’ll use to make our POST request to that URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    const getParams = () =&amp;gt; {
    const params = (new URL(window.location)).searchParams;
    const approvalStatus = params.get("code") ? params.get("code") : false;

    if ( approvalStatus !== false ) {
      UserStore.getAccessToken(approvalStatus).then(res =&amp;gt; {
        setCredentials({...credentials, access_token: res.access_token, refresh_token: res.refresh_token})
      }).catch(console.log);
      setFlow(2);
      setApproved(true);
    } 
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case it helps you, there is my code that parses the URL, then does it’s thing in terms of parsing the URL and deciding what to do after.&lt;br&gt;
It then calls another function which is the meat and potatoes of our auth flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  getAccessToken = async (token) =&amp;gt; {
    if (!token) return null;
    const encode = window.btoa(`${process.env.REACT_APP_REDDIT_APP_NAME}:${process.env.REACT_APP_REDDIT_APP_SECRET}`);
    const redditTokens = await Axios.post('https://www.reddit.com/api/v1/access_token', 
      `grant_type=authorization_code&amp;amp;code=${token}&amp;amp;redirect_uri=${process.env.REACT_APP_REDDIT_REDIRECT}/signup`,
    {
      headers: {
        "Authorization": `Basic ${encode}`,
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    })
    .then(res =&amp;gt; {
      if (res.data.error) {
        return toast.error("Please re-authenticate");
      };
      return res.data;
    })
    .catch(console.log);


    return redditTokens;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this is successful, you’ll be golden, but only for an hour, then you’ll need to get another code.&lt;/p&gt;

&lt;p&gt;When we parse the URL and pull out the “code” parameter, we use that to get our access token which I passed into the above function.&lt;/p&gt;

&lt;p&gt;We have to make a POST request to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://www.reddit.com/api/v1/access_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where the body of the request is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grant_type=authorization_code&amp;amp;code=${token}&amp;amp;redirect_uri=${process.env.REACT_APP_REDDIT_REDIRECT}/signup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We take our code we previously got, and pass it into the code portion of the new URL (just a bunch of confirmations at this point). Make sure to read the OAuth2 documentation I mentioned at the very beginning of this article, it will help you know how to build the link to POST to.&lt;br&gt;
We have to make sure we also encode our app secret and app name and include that in our Authorization header. This was a bit tricky for me to understand at first, mainly how to encode it. They don’t show you flat out how to do it, but mainly allude to it. After some googling and help from a friend, we came to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.btoa(`${process.env.REACT_APP_REDDIT_APP_NAME}:${process.env.REACT_APP_REDDIT_APP_SECRET}`);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which works!&lt;/p&gt;

&lt;p&gt;The function getAccessToken is called once we receive the go ahead to authenticate with Reddit. Once that finishes, we are all set.&lt;/p&gt;

&lt;p&gt;The problem comes when, after an hour, we need to make another request to an OAuth endpoint. &lt;/p&gt;

&lt;p&gt;What I did was created a backend for my app which holds my user model. During the sign up process, I send the access token and refresh token, and save it to the database.&lt;/p&gt;

&lt;p&gt;I also created a function that retrieves those tokens. In another post, I’ll go over the auth flow with my actual backend and not Reddit.&lt;/p&gt;

&lt;p&gt;Every time my app loads, just for the sake of easiness, I call the function that refreshes my access token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const renewRefreshToken = async () =&amp;gt; {
  const encode = window.btoa(`${process.env.REACT_APP_REDDIT_APP_NAME}:${process.env.REACT_APP_REDDIT_APP_SECRET}`);
  const token = await fetchTokens();
  const jwt = window.localStorage.getItem('token');


  if ( !token || !token.access_token ) return null;


  await Axios.post('https://www.reddit.com/api/v1/access_token', 
    `grant_type=refresh_token&amp;amp;refresh_token=${token.refresh_token}`,
  {
    headers: {
      "Authorization": `Basic ${encode}`,
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  })
  .then(res =&amp;gt; {
    saveTokensToDb(res.data.access_token, token.refresh_token, jwt);
    getCurrentAuthenticatedUser(res.data.access_token);
  })
  .catch(console.log);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is calling the fetchTokens function and waiting for it to finish because what that does is, grabs our locally (localStorage) saved JWT token, decodes it on the backend, and uses the user ID to look up the user.&lt;/p&gt;

&lt;p&gt;It then returns both tokens, which allows the code to work. I haven’t run into any issues so far, doing it this way.&lt;/p&gt;

&lt;p&gt;Hopefully this sheds light on how to navigate the Reddit docs in a basic way, and hopefully it will help you on your way to authenticating with Reddit using JavaScript. I’ll be attempting to write more posts in this series, if you made it this far, thanks so much for reading!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>reddit</category>
      <category>api</category>
      <category>react</category>
    </item>
  </channel>
</rss>
