<?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>DEV Community: tieje</title>
    <description>The latest articles on DEV Community by tieje (@tieje).</description>
    <link>https://dev.to/tieje</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%2F717271%2F17e17154-42a5-4a19-a56f-fb15e446f8fd.png</url>
      <title>DEV Community: tieje</title>
      <link>https://dev.to/tieje</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tieje"/>
    <language>en</language>
    <item>
      <title>How to Get the URL for Pictures</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Thu, 14 Apr 2022 17:50:28 +0000</pubDate>
      <link>https://dev.to/tieje/how-to-get-the-url-for-pictures-30o6</link>
      <guid>https://dev.to/tieje/how-to-get-the-url-for-pictures-30o6</guid>
      <description>&lt;ul&gt;
&lt;li&gt;In your favorite search engine, type whatever you want a picture of and press &lt;code&gt;Enter&lt;/code&gt; (Windows) or &lt;code&gt;return&lt;/code&gt; (Mac).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uxx-w8l1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/PgCyL8Y/Screen-Shot-2022-04-14-at-1-39-49-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uxx-w8l1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/PgCyL8Y/Screen-Shot-2022-04-14-at-1-39-49-PM.png" alt="duck enter" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Left-click on the search engine's &lt;code&gt;Image&lt;/code&gt; tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wogqG3mP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/y4DgdV2/Screen-Shot-2022-04-14-at-1-40-05-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wogqG3mP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/y4DgdV2/Screen-Shot-2022-04-14-at-1-40-05-PM.png" alt="duck images" width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Left-click on any picture to bring it up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OW-1q1Ry--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/Yj416vY/Screen-Shot-2022-04-14-at-1-40-47-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OW-1q1Ry--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/Yj416vY/Screen-Shot-2022-04-14-at-1-40-47-PM.png" alt="Duck left click" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Right-click the picture for options and left-click &lt;code&gt;Copy Image Address&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zn-qwW5d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/Bn2gP1p/Screen-Shot-2022-04-14-at-1-41-09-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zn-qwW5d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/Bn2gP1p/Screen-Shot-2022-04-14-at-1-41-09-PM.png" alt="Duck right click" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Press &lt;code&gt;ctrl + v&lt;/code&gt; (Windows) or &lt;code&gt;Command + v&lt;/code&gt; (Mac) to paste the image address wherever you need it.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tutorial</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Skill Tree Update 4/11/2022</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Tue, 12 Apr 2022 02:48:45 +0000</pubDate>
      <link>https://dev.to/tieje/skill-tree-update-4112022-2068</link>
      <guid>https://dev.to/tieje/skill-tree-update-4112022-2068</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mzk6ZlKg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/YRvhHJS/Screen-Shot-2022-04-11-at-10-09-11-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mzk6ZlKg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/YRvhHJS/Screen-Shot-2022-04-11-at-10-09-11-PM.png" alt="app so far" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Two Steps Forward One Step Back&lt;/li&gt;
&lt;li&gt;Work and Life Balance with Weekend Wednesdays&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Your greatest enemy is yourself from 10 hours ago."&lt;br&gt;
-- &lt;cite&gt;&lt;a href="https://www.factorio.com/"&gt;&lt;em&gt;Factorio&lt;/em&gt;&lt;/a&gt; players&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  One of the goals of this week was to add &lt;a href="https://blog.dreamfactory.com/stateful-vs-stateless-web-app-design/"&gt;statefulness&lt;/a&gt; to the skill tree app. Fortunately, &lt;a href="https://github.com/chrvadala/react-svg-pan-zoom"&gt;pan and zoom&lt;/a&gt; works perfectly fine out of the box. &lt;a href="https://dev.to/tieje/how-to-listen-to-events-in-reactjs-42a8"&gt;Event listeners&lt;/a&gt; for keyboard shortcuts were actually pretty easy to add. One portion that I struggled with was experimenting with &lt;a href="https://stackoverflow.com/questions/31089221/what-is-the-difference-between-put-post-and-patch"&gt;PUT, PATCH, and POST methods&lt;/a&gt; using RTK Query on the frontend. Despite understanding how it all works, there's that nagging feeling in the back my head that's like, &lt;em&gt;there's no way this is going to work&lt;/em&gt;. But it did work. I have CRUD functionality now!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S3Lp7sS2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/7zCHgp8/Screen-Shot-2022-04-11-at-10-10-12-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S3Lp7sS2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/7zCHgp8/Screen-Shot-2022-04-11-at-10-10-12-PM.png" alt="CRUD functionality" width="710" height="1570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uwzQwo-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/MMynSmf/Screen-Shot-2022-04-11-at-10-11-49-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uwzQwo-2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/MMynSmf/Screen-Shot-2022-04-11-at-10-11-49-PM.png" alt="Update" width="710" height="1104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y9K9Z0NN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/d6vv61t/Screen-Shot-2022-04-11-at-10-37-42-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y9K9Z0NN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/d6vv61t/Screen-Shot-2022-04-11-at-10-37-42-PM.png" alt="Backend" width="800" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Steps Forward One Step Back
&lt;/h2&gt;

&lt;p&gt;  The big mistake I made was using multiple sources of state. There should only be one source of truth in state management. Ultimately, this mistake led to not being able to add a new hexagon on the fly without reloading the window. I could not invalidate the query because I loaded my state from a slice and not from a &lt;code&gt;useQuery&lt;/code&gt; hook. I needed to implement a &lt;code&gt;useQuery&lt;/code&gt; hook from RTK Query so that I can invalidate the query and it can grab data from the API. I was using RTK Query incorrectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work and Life Balance with Weekend Wednesdays
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dJ0WFBBW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.wixstatic.com/media/cd4386_579573160a8a4c6c8a09d307851397e0%257Emv2.png/v1/fill/w_1000%2Ch_563%2Cal_c%2Cusm_0.66_1.00_0.01/cd4386_579573160a8a4c6c8a09d307851397e0%257Emv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dJ0WFBBW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static.wixstatic.com/media/cd4386_579573160a8a4c6c8a09d307851397e0%257Emv2.png/v1/fill/w_1000%2Ch_563%2Cal_c%2Cusm_0.66_1.00_0.01/cd4386_579573160a8a4c6c8a09d307851397e0%257Emv2.png" alt="Weekend Wednesdays" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  &lt;a href="https://www.youtube.com/watch?v=ALaTm6VzTBw"&gt;Weekend Wednesday&lt;/a&gt; is making Wednesday a day-off and making Saturday a work-day. Knowledge workers may want to adopt this schedule because our performance diminishes significantly as days go on without a day of rest. This week marks my first weekend Wednesday trial and I already feel significantly more productive and happier than on the old Monday through Friday schedule.&lt;/p&gt;

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

&lt;p&gt;  Last week, I learned a lot about working with queries in general. CRUD functionality for hexagons is now complete. This week I'll be moving on to CRUD functionality for paths. Now that I understand queries, I feel like frontend development should be smooth sailing from here on out.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>react</category>
      <category>redux</category>
    </item>
    <item>
      <title>How to Delete a Table from a Database in Django</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Mon, 04 Apr 2022 19:29:33 +0000</pubDate>
      <link>https://dev.to/tieje/how-to-delete-a-table-from-a-database-in-django-4bih</link>
      <guid>https://dev.to/tieje/how-to-delete-a-table-from-a-database-in-django-4bih</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://github.com/tieje/delete_django_table"&gt;Solution Link&lt;/a&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;How to delete a single table in Django 4.0:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remove &amp;lt;YourDeleteTable&amp;gt; model from &lt;code&gt;models.py&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Remove &amp;lt;YourDeleteTable&amp;gt; class from &lt;code&gt;admin.py&lt;/code&gt; file and ALL other instances of wherever this class is used.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python manage.py makemigrations &amp;lt;&amp;lt;your app&amp;gt;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python manage.py migrate&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I wrote this guide because I lost all my data from following a different guide on Stack Overflow. T_T&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.dev2qa.com/how-to-drop-change-tables-from-sqlite3-database-in-django/"&gt;Solutions to Additional Database Problems in Django&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Skill Tree Update 4/4/2022</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Mon, 04 Apr 2022 18:04:06 +0000</pubDate>
      <link>https://dev.to/tieje/skill-tree-update-442022-2e5b</link>
      <guid>https://dev.to/tieje/skill-tree-update-442022-2e5b</guid>
      <description>&lt;h2&gt;
  
  
  Content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Building the Static UI&lt;/li&gt;
&lt;li&gt;Building the Backend Core in a Single Weekend&lt;/li&gt;
&lt;li&gt;Adding Statefulness with Redux Toolkit&lt;/li&gt;
&lt;li&gt;How I Approach My Work Now&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JvIiacaR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/DpFpsJw/Screen-Shot-2022-04-01-at-2-45-10-PM-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JvIiacaR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/DpFpsJw/Screen-Shot-2022-04-01-at-2-45-10-PM-2.png" alt="April Fool's update" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;  It's been a ride. &lt;a href="https://dev.to/tieje/project-skill-tree-tech-stack-1274"&gt;I remember three weeks ago when I was starry-eyed about incorporating new technologies like NextJS and FaunaDB&lt;/a&gt;. What ended up happening is I fell back to  technologies that I was familiar with: Django for REST APIs and PostgreSQL. Part of the reason why I wanted to use new technologies was because it was frustrating getting my development environment working with an M1 Mac Air. Fortunately, &lt;a href="https://dev.to/tieje/how-to-install-psycopg2-on-an-m1-mac-mie"&gt;I figured it out&lt;/a&gt;. Building the static UI was a breeze; it only took a day. Making the static UI stateful, well that took the next two weeks. The following is  an overview of the major updates I have accomplished so far. The most important update being how I approach my work now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Static UI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FMEJyi3D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/Vg4Z3sG/Screen-Shot-2022-04-01-at-3-18-12-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FMEJyi3D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/Vg4Z3sG/Screen-Shot-2022-04-01-at-3-18-12-PM.png" alt="static ui" width="476" height="971"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  The UI was built using Tailwind CSS in a &lt;code&gt;create-react-app&lt;/code&gt; TypeScript template with a box of scraps. Because I already designed how it would &lt;a href="https://i.ibb.co/RTRSTyp/Screen-Shot-2022-03-29-at-8-43-14-PM.png"&gt;basically look&lt;/a&gt;, the biggest issue I ran into while creating the static UI was determining if I should add a submit button. Unfortunately, this is not an April Fool's joke. This UI decision affects how often I will send updates to the API. Is it better to do many small updates or one big update to the API? Said in a different way: How many trips to the grocery store do you want to make? Exactly, one big update with a single submit button would be the most optimal method of updating the API. However, it would also be more cumbersome and &lt;em&gt;dangerous&lt;/em&gt; for most people. Yes, &lt;strong&gt;dangerous&lt;/strong&gt;, and I'll explain why.&lt;/p&gt;

&lt;p&gt;  Having a single Edit/Submit button would open all forms to change at once. Changing one part of the form and accidentally changing another part of the form can cause a serious error in miscommunication of ideas or loss of work. A single Edit/Submit button &lt;em&gt;can&lt;/em&gt; streamline work, however, it is not sufficiently safe for our purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Backend Core in a Single Weekend
&lt;/h2&gt;

&lt;p&gt;  When I'm tasked with creating an API with batteries included as quickly as possible, I fall back on Django. Is it because it's the best backend? Nope. It's because it's the backend that I understand the best and because it's the first web framework I learned. And honestly, if I had to code in a single language for the rest of life, it would probably be Python.&lt;/p&gt;

&lt;p&gt;  As I mentioned before, &lt;a href="https://dev.to/tieje/how-to-install-psycopg2-on-an-m1-mac-mie"&gt;the hardest part of setting up the backend&lt;/a&gt; was making sure that when I migrate to a PostgreSQL database, Django's ORM can communicate with it through &lt;a href="https://www.psycopg.org/"&gt;psycopg2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;  The backend core features REST APIs that I will need to use for testing data fetching and data fetching techniques on the frontend with Redux Toolkit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Statefulness with Redux Toolkit
&lt;/h2&gt;

&lt;p&gt;  It if weren't for Redux Toolkit, I probably would have used a different state manager. Vanilla Redux without Toolkit is fairly difficult to figure out on a single pass of reading the documentation. Fortunately, I had built a fake Redux replica from a book tutorial prior so I already had good idea on how Redux worked under the hood. I can safely recommend using Redux Toolkit and its data-fetching counterpart, Redux Toolkit Query. State management has been a breeze with Redux Toolkit.&lt;/p&gt;

&lt;p&gt;From all the hype of React Query, I thought I could use it to replace Redux, but it &lt;a href="https://react-query.tanstack.com/guides/does-this-replace-client-state"&gt;clearly says on its website&lt;/a&gt; that it is &lt;em&gt;not&lt;/em&gt; a replacement for state management systems like Redux. I spent a good five hours comparing Redux and React Query only to realize that React Query is not the state management killer that I thought it was.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Approach My Work Now
&lt;/h2&gt;

&lt;p&gt;  As I struggled to fall asleep after a stressful sprint of three consecutive 14-hour workdays, Cal Newport's book, &lt;em&gt;Deep Work&lt;/em&gt;, popped into my head. &lt;a href="https://blog.doist.com/deep-work/"&gt;Deep Work is focusing solely on knowledge work without distractions&lt;/a&gt;. I've known about it since its inception, but I gave up on it several times in the past due to lack of motivation. I could not possibly sustain my self-destructive work schedule, so I put my phone on &lt;em&gt;Do Not Disturb&lt;/em&gt;, I abstained from reading news, emails, or watching youtube videos. And I politely told my family not to disturb me from 8am to 12pm. The general limit for deep work is about four hours. The result: I felt very productive and I was able to finish at least one or two project goals per day.&lt;/p&gt;

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

&lt;p&gt;  From these past three weeks, I needed to solve many technical problems, but also human problems like fixing how I approach work. In the future, I'd like to recognize my own personal human problems sooner so I don't get in the way of my work.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>product</category>
      <category>redux</category>
    </item>
    <item>
      <title>How to Create Nested JSON in Django REST APIs</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Fri, 01 Apr 2022 18:39:13 +0000</pubDate>
      <link>https://dev.to/tieje/how-to-create-nested-json-in-django-rest-apis-430c</link>
      <guid>https://dev.to/tieje/how-to-create-nested-json-in-django-rest-apis-430c</guid>
      <description>&lt;h2&gt;
  
  
  Content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;My Code as an Example&lt;/li&gt;
&lt;li&gt;Resulting JSON&lt;/li&gt;
&lt;li&gt;Helpful Links&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;  Nesting JSON with the &lt;a href="https://www.django-rest-framework.org/"&gt;Django REST Framework&lt;/a&gt; is not intuitive, but it is approachable once learned. Utilize the &lt;code&gt;serializers.SerializerMethodField()&lt;/code&gt; from the &lt;code&gt;rest_framework&lt;/code&gt;. By default, this method will call a method that begins with &lt;code&gt;get_{your_variable_name}&lt;/code&gt;. From here, &lt;a href="https://docs.djangoproject.com/en/4.0/ref/models/querysets/#django.db.models.query.QuerySet"&gt;Django's QuerySet API&lt;/a&gt; can help you reduce multiple lines of a SQL into a single line of Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Code as an Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QuerySet&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;skilltree.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SkillTrees&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SkillTreeHexagons&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SkillTreePaths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SkillTreeHexagonNotes&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;drf_queryfields&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QueryFieldsMixin&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SkillTreesSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QueryFieldsMixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelSerializer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SkillTrees&lt;/span&gt;
        &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__all__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SerializerMethodField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;hexagons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SerializerMethodField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;hex_string_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SerializerMethodField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_hexagons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SkillTrees&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;QuerySet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;
        Get all hex ids that have the skill_tree_id
        The object is the SkillTrees instance
        For example, print(obj.skill_tree_id) will print each skill tree instance&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s skill_tree_id
        &lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;
        &lt;span class="n"&gt;hexagons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;QuerySet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SkillTreeHexagons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;skill_tree_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skill_tree_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hexagons&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SkillTrees&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;QuerySet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;Get all hex ids that have the skill_tree_id&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;
        &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;QuerySet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SkillTreePaths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;skill_tree_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skill_tree_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;paths&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_hex_string_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SkillTrees&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;QuerySet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;Collect all hex string ids&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;
        &lt;span class="n"&gt;hex_string_ids_obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;QuerySet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SkillTreeHexagons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;skill_tree_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skill_tree_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;values_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hex_string&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hex_string_ids_obj&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resulting JSON
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"skill_tree_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"skill_tree_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"starting_hex_q"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"starting_hex_r"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"starting_hex_s"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ending_hex_q"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ending_hex_r"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ending_hex_s"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"starting_hex_string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0,0,0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ending_hex_string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2,0,-2"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"skill_tree_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"starting_hex_q"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"starting_hex_r"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"starting_hex_s"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ending_hex_q"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ending_hex_r"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ending_hex_s"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"starting_hex_string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0,0,0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ending_hex_string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0,2,-2"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hexagons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"note_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_q"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_r"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_s"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"skill_tree_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allow_verbal_feedback"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allow_quantitative_feedback"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"image_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.knowol.com/wp-content/uploads/2017/02/abraham-lincoln-1863.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1,1,1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"note_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_q"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_r"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_s"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"skill_tree_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allow_verbal_feedback"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allow_quantitative_feedback"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"image_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://thecolloquial.com/wp-content/uploads/2020/07/Thomas_Paine_rev1-e1593883311631-319x180.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hex_string"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2,2,2"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hex_string_list"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;"1,1,1"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"2,2,2"&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test tree"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yagami"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Helpful Links Section
&lt;/h2&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://stackoverflow.com/questions/65540356/creating-a-nested-json-from-a-value-in-django-rest-framework"&gt;Stack Overflow link that helped me&lt;/a&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield"&gt;Django REST Framework documentation on serializers&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>django</category>
      <category>rest</category>
      <category>api</category>
      <category>json</category>
    </item>
    <item>
      <title>How to Install psycopg2 on an M1 Mac</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Fri, 25 Mar 2022 01:21:04 +0000</pubDate>
      <link>https://dev.to/tieje/how-to-install-psycopg2-on-an-m1-mac-mie</link>
      <guid>https://dev.to/tieje/how-to-install-psycopg2-on-an-m1-mac-mie</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR: The Solution
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;The Solution&lt;/li&gt;
&lt;li&gt;Avoid These Potential Solutions&lt;/li&gt;
&lt;li&gt;Workarounds&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;  I remember a year ago, when I could not solve this problem for a different project so I ended up using a Docker container to emulate a simple linux environment to use &lt;code&gt;psycopg2&lt;/code&gt;. I'm a bit older and wiser now, so when faced with the same problem for &lt;a href="https://dev.to/tieje/the-beginning-of-project-skill-tree-1je2"&gt;my current project&lt;/a&gt;, I dug around all day today figuring out how to install &lt;code&gt;psycopg2&lt;/code&gt; in an M1 Mac environment. I tried many solutions that may have worked for others somehow, but ultimately did not work me. It turns out that the solution was the most vanilla installation I've done since I started using my M1 Mac Air.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Uninstall &lt;code&gt;pyenv&lt;/code&gt;, &lt;code&gt;pipx&lt;/code&gt;, and all other versions of &lt;code&gt;python&lt;/code&gt; except your system's python.&lt;/li&gt;
&lt;li&gt;Download the latest version of python from the &lt;a href="https://www.python.org/downloads/release/python-3104/"&gt;python website&lt;/a&gt;. At the time of writing, I was downloading python &lt;code&gt;3.10.4&lt;/code&gt; so look around to see if there is an even more updated version.&lt;/li&gt;
&lt;li&gt;Run the installer. Once it's finished, if you check your Applications, you should have your new version of python there. To test your new installation, run &lt;code&gt;python3 --version&lt;/code&gt; in a fresh terminal.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;pip3 install psycopg2-binary&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Optional Steps
&lt;/h3&gt;

&lt;p&gt;  Optionally, you can also install PostgreSQL on your M1 mac as well.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the postgreSQL installation from the &lt;a href="https://www.postgresql.org/download/"&gt;official website&lt;/a&gt;. At the time of writing, the most recent version, PostgreSQL 14, looks like it has the most support for the M1 Mac architecture.&lt;/li&gt;
&lt;li&gt;Run the installation wizard. The default location for my installation was &lt;code&gt;/Library/PostgreSQL/14&lt;/code&gt;, so I just used that.&lt;/li&gt;
&lt;li&gt;Add PostgreSQL bin to PATH in your &lt;code&gt;.zshrc&lt;/code&gt; file. It should look like the following:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/your/path/to/PostgreSQL/14/bin:&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;PATH"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;  Alternatively, you can can use this command if your installation was made in the same folder as mine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="/Library/PostgreSQL/14/bin:$PATH"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Open a new terminal and run &lt;code&gt;pg_config&lt;/code&gt; in the command line. Your configuration should come up.&lt;/li&gt;
&lt;li&gt;To get started on your Django application, PostgreSQL offers a &lt;a href="https://www.enterprisedb.com/postgres-tutorials/how-use-postgresql-django"&gt;brief tutorial&lt;/a&gt;, which is actually useful later on when you're replacing the initial SQLite database in settings.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Avoid These Potential Solutions
&lt;/h2&gt;

&lt;p&gt;  Avoid using a version manager like &lt;code&gt;pyenv&lt;/code&gt; or &lt;code&gt;asdf&lt;/code&gt;. If you install &lt;code&gt;psycopg2&lt;/code&gt; with this configuration, you will likely run into architectural issues with &lt;code&gt;arm64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;  Avoid using &lt;code&gt;Homebrew&lt;/code&gt; or anything that was installed with &lt;code&gt;Homebrew&lt;/code&gt;. &lt;code&gt;openssl&lt;/code&gt;, &lt;code&gt;libpq&lt;/code&gt;, and other TLS/SSL packages will not be sufficient substitutes for the lack of SSL support for Python. Even adding a symlink to the specific libraries needed will not be sufficient enough to solve the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workarounds
&lt;/h2&gt;

&lt;p&gt;  As I mentioned in the introduction, you &lt;em&gt;can&lt;/em&gt; use Docker to containerize your application and ultimately install &lt;code&gt;psycopg2&lt;/code&gt;. But it's not the simplest or the fastest solution. If you must do it this way, here a few helpful links to the Github project that I was working on at the time to help you get started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tieje/yorha#Docker"&gt;My Tutorial of getting started with Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tieje/yorha/blob/main/Backend/Dockerfile"&gt;My &lt;code&gt;Dockerfile&lt;/code&gt; used for the &lt;code&gt;Django&lt;/code&gt; Backend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tieje/yorha/blob/main/docker-compose.override.yml"&gt;My &lt;code&gt;docker-compose.yml&lt;/code&gt; file used for the Frontend and Backend&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;  I hope this helps. It appears that Apple's M1 chip has received a ton of support from the developer community over the past year, which makes me less inclined to sell my Mac Air for a &lt;a href="https://frame.work/"&gt;Framework laptop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;  I can only speculate that Apple wants to eliminate the need for 3rd-party package managers and version managers like &lt;code&gt;Homebrew&lt;/code&gt;, and &lt;code&gt;pyenv&lt;/code&gt; and &lt;code&gt;asdf&lt;/code&gt;, respectively. The newest update, Monterey 12.3, is hostile to 3rd party programs especially if Xcode is not installed. &lt;a href="https://macresearch.org/fix-cant-be-opened-because-apple-cannot-check-it-for-malicious-software/"&gt;It's not impossible to install and use 3rd-party applications but there will be security pop-up that needs a little more than a password to get around.&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>postgres</category>
      <category>django</category>
      <category>osx</category>
    </item>
    <item>
      <title>How to Listen to Events in ReactJS</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Thu, 24 Mar 2022 01:48:32 +0000</pubDate>
      <link>https://dev.to/tieje/how-to-listen-to-events-in-reactjs-42a8</link>
      <guid>https://dev.to/tieje/how-to-listen-to-events-in-reactjs-42a8</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/donavon/use-event-listener"&gt;React Hook Solution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.querythreads.com/listen-to-keypress-for-document-in-reactjs/"&gt;Original Discussion Thread&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;How It Works&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;  In my &lt;a href="https://dev.to/tieje/i-wasted-two-days-jerry-rigging-a-package-when-the-solution-was-one-search-away-1ikp#conclusion"&gt;last article&lt;/a&gt;, I created a static hexagonal grid with pan and zoom functionality. That's cool, but what's not cool is needing to press the one of the follow buttons to switch between &lt;em&gt;pointer mode&lt;/em&gt; and &lt;em&gt;drag mode&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8b1K33ph--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/TBFkGCZ/Screen-Shot-2022-03-23-at-9-43-31-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8b1K33ph--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/TBFkGCZ/Screen-Shot-2022-03-23-at-9-43-31-PM.png" alt="SVG app tools" width="70" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  Instead of pressing these toolbar buttons, I'd like to use a keyboard shortcut to switch between the &lt;em&gt;pointer mode&lt;/em&gt; and the &lt;em&gt;drag mode&lt;/em&gt;. Copying Figma's button shortcuts, specifically, I would like to bind the &lt;code&gt;v&lt;/code&gt; button and the &lt;code&gt;h&lt;/code&gt; button keyboard keys to the &lt;em&gt;pointer mode&lt;/em&gt; and &lt;em&gt;drag mode&lt;/em&gt;, respectively. This functionality was achieved thanks to the &lt;a href="https://github.com/donavon/use-event-listener"&gt;&lt;code&gt;use-event-listener&lt;/code&gt;&lt;/a&gt; hook. Fortunately, the React hook is simple enough to use as brief case study into how event listeners work.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;  The &lt;code&gt;useEventListener&lt;/code&gt; hook is only one file with a brief amount of code:&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="cm"&gt;/* eslint-disable max-params */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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;useEventListener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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;savedHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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;capture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;passive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&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;savedHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;handler&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;handler&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nf"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSupported&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&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;isSupported&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="p"&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;eventListener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;savedHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;passive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventListener&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return &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;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventListener&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;passive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&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;useEventListener&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;cite&gt;&lt;a href="https://github.com/donavon/use-event-listener/blob/develop/src/index.js"&gt;donavan's original code&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  Let's break it down.&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;useEventListener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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;savedHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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;capture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;passive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;  The &lt;code&gt;useEventListener&lt;/code&gt; requires the event name and the handler function. In my case, the event I'm looking for is &lt;code&gt;keypress&lt;/code&gt; and the function I made is &lt;code&gt;handlePanZoomModeSwitch&lt;/code&gt;&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="nf"&gt;useEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keypress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handlePanZoomModeSwitch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;  Because I'm simply checking if the keyboard &lt;code&gt;v&lt;/code&gt; or &lt;code&gt;h&lt;/code&gt; key is pressed, it's perfectly fine to use the default element &lt;code&gt;global&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;  There are three available options, despite &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener"&gt;four possible options&lt;/a&gt;. None of them fit my needs, however.&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="nf"&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;savedHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;handler&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;handler&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;  &lt;code&gt;savedhandler&lt;/code&gt; is assigned to the &lt;code&gt;useRef()&lt;/code&gt; &lt;a href="https://learnreact.design/posts/react-useref-by-example"&gt;which is used to access DOM nodes and persist mutable values across re-renders&lt;/a&gt;. In our, case we don't want to forget whatever state is already attached to the current DOM in our window. Since the second &lt;code&gt;useEffect()&lt;/code&gt; parameter is specified as &lt;code&gt;[handler]&lt;/code&gt;, whenever the handler function changes, the component will re-render itself. If the second &lt;code&gt;useEffect()&lt;/code&gt; parameter was not specified, as in simply being &lt;code&gt;[]&lt;/code&gt;, then the component will only render the component once.&lt;/p&gt;

&lt;p&gt;  The last useEffect hook looks lengthy, but it's not that complex. The &lt;code&gt;isSupported&lt;/code&gt; if-statement just checks if the element exists and if we can add an event listener to that element.&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="nf"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSupported&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&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;isSupported&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="p"&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;eventListener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;savedHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;passive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventListener&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return &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;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventListener&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;passive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;  Next, the &lt;code&gt;eventListener&lt;/code&gt; arrow function serves as the handler function for the addEventListener. The &lt;code&gt;eventListener&lt;/code&gt; function simply passes whatever event occurs to the handler function that we specified.&lt;/p&gt;

&lt;p&gt;  &lt;a href="https://stackoverflow.com/questions/53256662/react-why-should-i-remove-event-listeners"&gt;Finally, the &lt;code&gt;removeEventListener()&lt;/code&gt; is passed to prevent memory leaks, side-effects, and event collisions.&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The &lt;code&gt;useEventListener()&lt;/code&gt; hook makes it easy to bind keys for your web app needs. Have fun out there!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>I wasted two days jerry-rigging a package when the solution was one search away</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Tue, 22 Mar 2022 19:59:43 +0000</pubDate>
      <link>https://dev.to/tieje/i-wasted-two-days-jerry-rigging-a-package-when-the-solution-was-one-search-away-1ikp</link>
      <guid>https://dev.to/tieje/i-wasted-two-days-jerry-rigging-a-package-when-the-solution-was-one-search-away-1ikp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"I attempted to jerry-rig this package for two days before giving up..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/tieje/the-beginning-of-project-skill-tree-1je2"&gt;Mission Statement&lt;/a&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;How to Look for Solutions&lt;/li&gt;
&lt;li&gt;Lessons Learned&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;  Developers use open source software all the time. They allow us to build products quickly and easily. Figuring out which software to use is a task that in itself requires in-depth knowledge about how these technologies work. The following is my experience in building a pan and zoom network graph and what I learned from the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Look for Solutions
&lt;/h2&gt;

&lt;p&gt;Open source solutions range from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is exactly what I needed, the documentation and examples are helpful, and it works.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  to&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While the documentation is poor, it should be hackable enough for me to build something with it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  An example of the former while building the network graph, was &lt;a href="https://github.com/Hellenic/react-hexgrid"&gt;&lt;code&gt;react-hexgrid&lt;/code&gt;&lt;/a&gt;. The documentation was unfinished, but what sold me were the examples and the simplicity of the code.&lt;/p&gt;

&lt;p&gt;  However, if your thought is the latter, then you should probably keep looking for a better solution. One could certainly hack their way to a solution, but unless you've been through almost everything, there is likely a better solution just around the corner.&lt;/p&gt;

&lt;p&gt;  For example, at one point I needed to solve two problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scroll by grabbing in the browser with the mouse.&lt;/li&gt;
&lt;li&gt;Zooming with a mouse wheel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;  At first, I was going to use &lt;a href="https://github.com/norserium/react-indiana-drag-scroll"&gt;&lt;code&gt;react-indiana-drag-scroll&lt;/code&gt;&lt;/a&gt; to solve the first problem. And it worked. But I had a feeling that there might be a solution that solved both of these problems at the same time. Searching on &lt;a href="https://www.libhunt.com/"&gt;libhunt&lt;/a&gt;, &lt;a href="https://openbase.com/"&gt;openbase&lt;/a&gt;, and &lt;a href="https://duckduckgo.com/"&gt;duckduckgo&lt;/a&gt;, I found out that this problem is actually very old and the term that better fit my problem was "pan and zoom". Searching with this term, I stumbled upon &lt;a href="https://github.com/woutervh-/react-pan-and-zoom-hoc"&gt;&lt;code&gt;react-pan-and-zoom-hoc&lt;/code&gt;&lt;/a&gt;, which helped solve my problem, but it was difficult to fidget with because rather than having a simple component, the component spit out the correct x, y axes translation that would need to be worked into the component's CSS. No bueno. I attempted to jerry-rig this package for two days before giving up and switching to &lt;a href="https://github.com/chrvadala/react-svg-pan-zoom"&gt;&lt;code&gt;react-svg-pan-zoom&lt;/code&gt;&lt;/a&gt;, which worked pretty much instantly and was exactly what I needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Don't settle. Taking a few days to find the right tool can save you weeks of fidgeting with a subpar solution.&lt;/li&gt;
&lt;li&gt;Using a variety of search terms and even different search engines and search applications are a must when looking for existing solutions. Most of the time, the problem has already been solved. This seems like a no-brainer, but I fall for this trap all the time.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;  That second point is important for another reason: &lt;a href="https://easyskilltree.com/"&gt;a version of the application I'm working on has already been created by someone else.&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z9iyoi6M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/hYVqbBM/Screen-Shot-2022-03-22-at-3-42-59-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z9iyoi6M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/hYVqbBM/Screen-Shot-2022-03-22-at-3-42-59-PM.png" alt="easyskilltree.com UI" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  However, the application looks rushed and unfinished. The UI is not what I had in mind.&lt;/p&gt;

&lt;p&gt;  Now that a static version of my network graph is basically finished, I'll start with building the instructor's interface since this deals with creating and storing data, which I haven't really figured out yet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--srDUTJoG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/GTmtTWs/Screen-Shot-2022-03-22-at-3-47-07-PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--srDUTJoG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/GTmtTWs/Screen-Shot-2022-03-22-at-3-47-07-PM.png" alt="Hexgrid UI" width="800" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  Am I really building a network graph with a hexagonal grid? Yes, and it's because of a &lt;a href="https://youtu.be/thOifuHs6eY"&gt;CGPGrey youtube video&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>react</category>
      <category>opensource</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Project Skill Tree: "Studying" Forem's Open Source Code for Markdown Features</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Fri, 11 Mar 2022 10:30:50 +0000</pubDate>
      <link>https://dev.to/tieje/project-skill-tree-studying-forems-open-source-code-for-markdown-features-4ghd</link>
      <guid>https://dev.to/tieje/project-skill-tree-studying-forems-open-source-code-for-markdown-features-4ghd</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/tieje/the-beginning-of-project-skill-tree-1je2"&gt;Mission Statement&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/tieje/project-skill-tree-designing-the-technical-frontend-part-1-178h#conclusion"&gt;Link to Why I'm Writing This Article&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Looking for the Forem Markdown Editing Feature

&lt;ul&gt;
&lt;li&gt;Problems with Option 1&lt;/li&gt;
&lt;li&gt;Problems with Option 2&lt;/li&gt;
&lt;li&gt;Problems with Option 3&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Answer&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;  The aim of this article is deduce how to extract the Markdown features used for article-writing from &lt;code&gt;dev.to&lt;/code&gt;. The website this article is hosted on, &lt;a href="https://dev.to/"&gt;dev.to&lt;/a&gt;, was built with open-source code produced by &lt;a href="https://www.forem.com/"&gt;Forem&lt;/a&gt;. I know that the Markdown feature is not specific to &lt;code&gt;dev.to&lt;/code&gt; because another website built with Forem, &lt;a href="https://www.thismmalife.com/"&gt;This MMA Life&lt;/a&gt;, which is about &lt;a href="https://en.wikipedia.org/wiki/Mixed_martial_arts"&gt;MMA&lt;/a&gt; and is thus unrelated to coding, uses the same Markdown document editing feature as &lt;code&gt;dev.to&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--26UaNJna--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/N7Q9vsT/Screen-Shot-2022-03-10-at-11-49-19-AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--26UaNJna--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/N7Q9vsT/Screen-Shot-2022-03-10-at-11-49-19-AM.png" alt="https://www.thismmalife.com/" width="800" height="759"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  That being said, let's try to find the Markdown editing feature in &lt;a href="https://github.com/forem/forem"&gt;Forem's source code&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking for the Forem Markdown Editing Feature
&lt;/h2&gt;

&lt;p&gt;  It's not particularly difficult to find the code used for writing Markdown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XDOmA3e4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/KjHssFZ/Screen-Shot-2022-03-11-at-4-06-21-AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XDOmA3e4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/KjHssFZ/Screen-Shot-2022-03-11-at-4-06-21-AM.png" alt="Markdown code" width="800" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  The only problem is that almost every file looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nJpIralY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/WcKTWxF/Screen-Shot-2022-03-11-at-4-10-20-AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nJpIralY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ibb.co/WcKTWxF/Screen-Shot-2022-03-11-at-4-10-20-AM.png" alt="production code" width="797" height="726"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  This is real, professional code, the likes of which would take me a month to fully understand. There are few approaches I can take with decreasing levels of difficulty:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I can read over the code, understand how it works, and attempt to replicate the most basic parts myself from scratch.&lt;/li&gt;
&lt;li&gt;I can read over the code, understand how it works, copy and paste what I need, and fix any bugs along the way.&lt;/li&gt;
&lt;li&gt;I can clone the entire repository, delete parts that I don't need, and fix any bugs along the way.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Problems with Option 1
&lt;/h2&gt;

&lt;p&gt;I will definitely learn the most here, but it's a month or more of learning on my own just to get started. It may be another month or more of coding the whole thing out based on what I've learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems with Option 2
&lt;/h2&gt;

&lt;p&gt;This is the middle ground, where I try to understand the code by copying only parts that I need. How much code is actually copy-pasted after all? I might learn some things but it's likely that none of it will stick around in my head, which will ultimately make debugging this large application harder in the long run. Despite expediting the process, I will end up with a lot of stress while still taking a month to figure out the code, copy-paste it, and debug it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems with Option 3
&lt;/h2&gt;

&lt;p&gt;  This would take a week to do and is definitely the most expedited option. But like option 2, I'll end up with a ton of &lt;strong&gt;tech debt&lt;/strong&gt; and will pay for it later in the form of a month of stress-inducing debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  What choice would you make?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wXSZ17PG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://amandakassner.com/wp-content/uploads/2019/04/suspense-image.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wXSZ17PG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://amandakassner.com/wp-content/uploads/2019/04/suspense-image.jpg" alt="Suspense" width="640" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Answer is...
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KGLDpW72--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media1.tenor.com/images/70f56083d8aeb3546d74cdc9c00e2ff8/tenor.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KGLDpW72--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://media1.tenor.com/images/70f56083d8aeb3546d74cdc9c00e2ff8/tenor.gif" alt="drum roll" width="498" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Secret Option 4 None of the Above
&lt;/h2&gt;

&lt;p&gt;  The markdown document feature goes on the list of features that are &lt;em&gt;nice to have in the future, but are not strictly necessary for basic functionality&lt;/em&gt;. I will be building an MVP after all. &lt;strong&gt;A simple text form will suffice for now.&lt;/strong&gt; In the meantime, I can look for libraries such as &lt;a href="https://www.libhunt.com/r/react-markdown"&gt;react-markdown&lt;/a&gt; that can potentially fulfill this feature later.&lt;/p&gt;

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

&lt;p&gt;  When building an MVP, it's important to not get bogged down by complex code. It's better to ship the MVP and either get hired by a company or present the basic idea to angel investors who care about education as much as I do. To most people, this seems like an a no-brainer. But like when all you have is a hammer, everything looks like a nail; likewise, when all you have is a keyboard, everything looks like an idea to be abstracted away with code. Many junior engineers make the mistake of biting off more than they chew and making large commitments to a codebase only to find out that no one wants to use their new JavaScript framework.&lt;/p&gt;

</description>
      <category>forem</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Project Skill Tree: Designing the Technical Frontend Part 1</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Thu, 10 Mar 2022 16:17:39 +0000</pubDate>
      <link>https://dev.to/tieje/project-skill-tree-designing-the-technical-frontend-part-1-178h</link>
      <guid>https://dev.to/tieje/project-skill-tree-designing-the-technical-frontend-part-1-178h</guid>
      <description>&lt;h2&gt;
  
  
  &lt;a href="https://dev.to/tieje/the-beginning-of-project-skill-tree-1je2"&gt;Mission Statement&lt;/a&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Two Separate Frontend App Layers&lt;/li&gt;
&lt;li&gt;Overlay UI Component Layer Features and Implementation&lt;/li&gt;
&lt;li&gt;Skill Tree Background Layer Features and Implementation&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;  Although I &lt;a href="https://dev.to/tieje/project-skill-tree-tech-stack-1274"&gt;defined the tech stack in a previous article&lt;/a&gt;, I will need to go in depth about &lt;em&gt;how&lt;/em&gt; features will be built. The more specific the better. As much as I would like to get straight to writing code, writing code for an application this large without thinking about implementation is a rookie mistake. I learned this from &lt;a href="http://semblance.us/"&gt;my last incomplete project (project Semblance)&lt;/a&gt; where I merely defined my tech stack instead of thinking of how features will be technically implemented and with what packages. There are tons of time-saving JavaScript libraries, packages, and alternatives out there: &lt;a href="https://www.libhunt.com/"&gt;here's a neat web app for finding alternatives&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Separate Frontend App Layers
&lt;/h2&gt;

&lt;p&gt;  The main application will consist of &lt;a href="https://www.figma.com/file/2HIVXfIRBTeEJy5QnTphSY/skill-tree?node-id=3%3A19"&gt;two layers: the overlay and the background&lt;/a&gt;. The overlay is the UI component used to interact with nodes in the background. When the user clicks on the skill tree background, the app will hide the overlay and allow the user to scroll around and zoom in and out of the skill tree background. This allows users to see the bigger picture like looking at a map from an RPG.&lt;/p&gt;

&lt;h3&gt;
  
  
  Template
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Feature

&lt;ul&gt;
&lt;li&gt;Implementation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Overlay UI Component Layer Features and Implementation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Markdown document, edit, and preview

&lt;ul&gt;
&lt;li&gt;Forem open source code&lt;/li&gt;
&lt;li&gt;perhaps an alternative library&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Search bar

&lt;ul&gt;
&lt;li&gt;Algola&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Menu drop down

&lt;ul&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Statistics bar graph

&lt;ul&gt;
&lt;li&gt;d3&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Overlay Node Data loading

&lt;ul&gt;
&lt;li&gt;NextJS lazyLoading&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Skill Tree Background Layer Features and Implementation
&lt;/h2&gt;

&lt;p&gt;  The easy way to build the skill tree background layer would be to separate nodes and lines from the background image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Background Image Layer
&lt;/h3&gt;

&lt;p&gt;  This seems to be the easy part.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Default Images

&lt;ul&gt;
&lt;li&gt;Static Assets&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Image Upload&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Node and Line Building
&lt;/h3&gt;

&lt;p&gt;  This is the hard part.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;default Node Icons

&lt;ul&gt;
&lt;li&gt;Static Assets&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;skill tree line and node building

&lt;ul&gt;
&lt;li&gt;d3&lt;/li&gt;
&lt;li&gt;Make nodes and lines three types of sizes and lengths, respectively, (short, medium, long)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;  In my last major project, I simply started coding and tried to make things work. The worst part of doing it this way for a large project is that you build out large parts of infrastructure only to rebuild it. This time, I'm probably going to try to use a sandbox like &lt;a href="https://codepen.io/features/"&gt;Code Pen&lt;/a&gt; to practice, test, and build out prototypes for d3, Forem (markdown document CRUD), and Algola (search engine?). I'm not sure about Algola since it might need a backend. I might not need Algola after all.&lt;/p&gt;

&lt;p&gt;  What I will do in the future is create a tutorial post for each feature that I'm figuring out.&lt;/p&gt;

</description>
      <category>design</category>
      <category>webdev</category>
      <category>nextjs</category>
      <category>fauna</category>
    </item>
    <item>
      <title>Project Skill Tree: Desktop and Mobile App View</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Wed, 09 Mar 2022 21:31:49 +0000</pubDate>
      <link>https://dev.to/tieje/project-skill-tree-desktop-and-mobile-app-view-1nn5</link>
      <guid>https://dev.to/tieje/project-skill-tree-desktop-and-mobile-app-view-1nn5</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.figma.com/file/2HIVXfIRBTeEJy5QnTphSY/skill-tree?node-id=3%3A19"&gt;Link to Figma work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Disclaimer&lt;/li&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Design Decisions&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;The color schema will be editable by the user. That being said, the initial Figma design is intended for design clarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;  Today, I designed the application pages of the &lt;a href="https://www.figma.com/file/2HIVXfIRBTeEJy5QnTphSY/skill-tree?node-id=3%3A19"&gt;teacher view and the mobile view&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Decisions
&lt;/h2&gt;

&lt;p&gt;  The student view served as an excellent template for the teacher view. Because the student and teacher views shared the same areas, a simplified mobile view will suffice.&lt;/p&gt;

&lt;p&gt;  I will need to keep in mind that the majority of people are most likely going to use their phones to access the website. I will need to focus on making an excellent mobile experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  TODOs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I wonder how hard it would be to turn this into a PWA (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps"&gt;progressive web app&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;  The application views are basically complete. It's time to get started with building the MVP.&lt;/p&gt;

</description>
      <category>ux</category>
      <category>webdev</category>
      <category>design</category>
      <category>product</category>
    </item>
    <item>
      <title>Project Skill Tree: Student View of App Page Part 1</title>
      <dc:creator>tieje</dc:creator>
      <pubDate>Tue, 08 Mar 2022 17:28:23 +0000</pubDate>
      <link>https://dev.to/tieje/project-skill-tree-student-view-of-app-page-part-1-jl7</link>
      <guid>https://dev.to/tieje/project-skill-tree-student-view-of-app-page-part-1-jl7</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Features&lt;/li&gt;
&lt;li&gt;TODOs&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;  Today, I designed the &lt;a href="https://www.figma.com/file/2HIVXfIRBTeEJy5QnTphSY/skill-tree?node-id=3%3A19"&gt;student view&lt;/a&gt; of the main application in Figma. The background placeholder is the skill tree from &lt;em&gt;Super Smash Bros. Ultimate&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;NavBar

&lt;ul&gt;
&lt;li&gt;Logo

&lt;ul&gt;
&lt;li&gt;Goes to home page&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Search Bar

&lt;ul&gt;
&lt;li&gt;Search for topic node&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Settings Button

&lt;ul&gt;
&lt;li&gt;Opens Student profile settings and viewing settings&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SideBar

&lt;ul&gt;
&lt;li&gt;Sidebar size must be adjustable.&lt;/li&gt;
&lt;li&gt;Topic Title&lt;/li&gt;
&lt;li&gt;Markdown document

&lt;ul&gt;
&lt;li&gt;Teacher's notes, links, and images written in markdown&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Edit Feedback button

&lt;ul&gt;
&lt;li&gt;Open the Feedback form&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Response Pop-Up

&lt;ul&gt;
&lt;li&gt;Rating Multiple choice

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Required&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Rate from "not well at all" to "very well" how well you feel like you understand a topic&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Written Feedback Form

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Optional&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Can explicitly tell the instructor how you're feeling about this topic&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Done Button

&lt;ul&gt;
&lt;li&gt;Pressing Done button causes the feedback form to hide&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TODOs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Come up with a color schema.

&lt;ul&gt;
&lt;li&gt;I should come up with several color schemas for the UI and leave that choice up to the user.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Think of how you're going to procedurally create the skill tree.&lt;/li&gt;
&lt;li&gt;Think of how you're going to implement user view-navigation on the skill tree.&lt;/li&gt;
&lt;li&gt;Think of the final visual design of the skill trees. I'm thinking of copying Skyrim.&lt;/li&gt;
&lt;li&gt;Think of node icon defaults&lt;/li&gt;
&lt;li&gt;Think of background image defaults. This will be the background beneath the skill tree.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;  It's a good start. I drew a lot of inspiration from the Figma UI itself, which I thought was funny. Using markdown for the instructor notes was inspired by my time writing posts at &lt;a href="https://dev.to/"&gt;dev.to&lt;/a&gt;. Markdown has the benefit of being useful whether teachers take advantage of its features or not.&lt;br&gt;
  I will be building the teacher view next. It should be as close to the student view as possible.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>ux</category>
      <category>product</category>
    </item>
  </channel>
</rss>
