<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Aaron Klaser on Medium]]></title>
        <description><![CDATA[Stories by Aaron Klaser on Medium]]></description>
        <link>https://medium.com/@aaron.klaser?source=rss-1ef30430402c------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*YB7IFnR9TO_H8dFhW0WdRQ@2x.jpeg</url>
            <title>Stories by Aaron Klaser on Medium</title>
            <link>https://medium.com/@aaron.klaser?source=rss-1ef30430402c------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 12 Nov 2026 11:09:51 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@aaron.klaser/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Testing Redux Sagas]]></title>
            <link>https://medium.com/hackernoon/testing-redux-sagas-c64dbba05935?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/c64dbba05935</guid>
            <category><![CDATA[redux-saga]]></category>
            <category><![CDATA[testing-sagas]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[unit-testing]]></category>
            <category><![CDATA[redux]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Fri, 10 Aug 2018 17:59:26 GMT</pubDate>
            <atom:updated>2018-08-31T19:11:05.391Z</atom:updated>
            <content:encoded><![CDATA[<p>As much as want to take credit for this post this was mostly written by <a href="https://medium.com/u/b5ac5e4556bf">John Pipkin</a>. It was real good and helped me a lot and I wanted it to be shared with the world.</p><p><a href="https://medium.com/@johnpipkin_79120/conceptualizing-unit-tests-with-redux-sagas-71c49fd8b08f?source=linkShare-1ef30430402c-1533944212">Click here for his full article</a></p><p>So testing generators it’s best to think of them as loops that execute when you tell them to.</p><h3>Step 1</h3><pre>it(‘should return 6’, () =&gt; {<br> // we’ve set up the generator, but we haven’t called next yet so we’re not at a yield<br> const gen = count()</pre><pre>expect(gen.next().value).toEqual(call(addNumber, number, 1))</pre><pre>expect(gen.next(1).value).toEqual(call(addNumber, number, 2))</pre><pre>expect(gen.next(3).value).toEqual(call(addNumber, number, 3))</pre><pre>expect(gen.next(6).value).toEqual(put(something(6)))<br>})</pre><pre>function *count() {<br> -&gt; // We haven’t told the generator to exicute anything yet so we’re not at a yield<br> let number = 0</pre><pre>number = yield call(addNumber, number, 1)<br> number = yield call(addNumber, number, 2) //3<br> number = yield call(addNumber, number, 3) //6<br> yield.put(something(number))<br>}</pre><p>In this step we haven’t told the generator to do anything yet, so we don’t have anything to test.</p><h3>Step 2</h3><pre>it(‘should return 6’, () =&gt; {<br> const gen = count()</pre><pre>// This next is called, we’re seeing what the yield will be<br> expect(gen.next().value).toEqual(call(addNumber, number, 1))</pre><pre>expect(gen.next(1).value).toEqual(call(addNumber, number,2))</pre><pre>expect(gen.next(3).value).toEqual(call(addNumber, number, 3))</pre><pre>expect(gen.next(6).value).toEqual(put(something(6)))<br>})</pre><pre>function *count() {<br> let number = 0</pre><pre>// Generator is paused here, waiting. The yield call(addNumber, number, 1) hasn’t executed<br> -&gt; number = yield call(addNumber, number, 1)<br> number = yield call(addNumber, number, 2) //3<br> number = yield call(addNumber, number, 3) //6<br> yield.put(something(number))<br>}</pre><p>Here we’ve executed gen.next(). We’ve told the generator to go the yield statement and wait. So now we’re paused at the -&gt;, but we haven’t actually ran the code.</p><p>When we get gen.next().value here we’re just getting what the yield WILL do.</p><h3>Step 3</h3><pre>it(‘should return 6’, () =&gt; {<br> const gen = count()</pre><pre>expect(gen.next().value).toEqual(call(addNumber, number, 1))</pre><pre>// called next here, this evaluated the previous line. We pass in the return value from the last line<br> expect(gen.next(1).value).toEqual(call(addNumber, number, 2))</pre><pre>expect(gen.next(3).value).toEqual(call(addNumber, number, 3))</pre><pre>expect(gen.next(6).value).toEqual(put(something(6)))<br>})</pre><pre>function *count() {<br> let number = 0</pre><pre>number = yield call(addNumber, number, 1)<br> // We evaluated the previous line, now were waiting at the this yield<br> -&gt; number = yield call(addNumber, number, 2) <br> number = yield call(addNumber, number, 3) //6<br> yield.put(something(number))<br>}</pre><p>Here we’ve called gen.next() again. The generator sees that command and executes the yield that it was paused at and continues the next `yield` statement. It will pause here -&gt;. gen.next() tells the yield statement it was at to execute, we can pass what we want returned from that into the call<br>We were paused at yield call(addNumber, number, 1), we wanted that to return 1 so we pass that in when we actually execute that line.</p><h3>Step 4</h3><pre>it(‘should return 6’, () =&gt; {<br> const gen = count()</pre><pre>expect(gen.next().value).toEqual(call(addNumber, number,1))</pre><pre>expect(gen.next(1).value).toEqual(call(addNumber, number, 2))</pre><pre>// Called next again, we evaluated the call to (number, 2) and specifiy its return value in this next<br> expect(gen.next(3).value).toEqual(call(addNumber, number, 3))</pre><pre>expect(gen.next(6).value).toEqual(put(something(6)))<br>})</pre><pre>function *count() {<br> let number = 0</pre><pre>number = yield call(addNumber, number, 1)<br> number = yield call(addNumber, number, 2) //3<br> -&gt; number = yield call(addNumber, number,3) //6<br> yield.put(something(number))<br>}</pre><p>This one is just like the previous step, just to illustrate what I’m talking about. The yield to call(addNumber, number, 2) should return 3 so we pass that in to the call to gen.next(3) because we’ve told the generator to execute that line and we want it to return 3.</p><h3>Step 5</h3><pre>it(‘should return 6’, () =&gt; {<br> const gen = count()</pre><pre>expect(gen.next().value).toEqual(call(addNumber, number, 1))</pre><pre>expect(gen.next(1).value).toEqual(call(addNumber, number, 2))</pre><pre>expect(gen.next(3).value).toEqual(call(addNumber, number, 3))</pre><pre>// Called next again, we evaluated the call to (number, 3) and specifiy its return value in this next.<br> // I’ve intentionally returned the wrong value from the call to (addNumber, number, 3) to illustrate how the return in next works<br> expect(gen.next(8).value).toEqual(put(something(8)))<br>})</pre><pre>function *count() {<br> let number = 0</pre><pre>number = yield call(addNumber, number, 1)<br> number = yield call(addNumber, number, 2) //3<br> number = yield call(addNumber, number, 3) //6<br> -&gt; yield.put(something(number))<br>}</pre><p>We’ve called gen.next() again so now we’re waiting at the yield to put. In this example you can see I intentionally returned the wrong value `8` in gen.next(). This is to illustrate that next takes in the return value of the previous execution. Essentailly mock data in this case. Now we see that the yield we’re paused at is the put(number). Since number in this case is 8 (because that’s what we told call(addNumber, number, 3) to return, that’s what we’re looking for.</p><h3>Step 6</h3><pre>it(‘should return 6’, () =&gt; {<br> const gen = count()</pre><pre>expect(gen.next().value).toEqual(call(addNumber, number, 1))</pre><pre>expect(gen.next(1).value).toEqual(call(addNumber, number, 2))</pre><pre>expect(gen.next(3).value).toEqual(call(addNumber, number, 3))</pre><pre>expect(gen.next(8).value).toEqual(put(something(8)))</pre><pre>// Finally call next again to see there are no more yields<br> expect(gen.next().done).toBeTruthy()<br>})</pre><pre>function *count() {<br> let number = 0</pre><pre>number = yield call(addNumber, number, 1)<br> number = yield call(addNumber, number, 2) //3<br> number = yield call(addNumber, number, 3) //6<br> yield.put(something(number))<br> -&gt;<br>}</pre><p>Finally, we call gen.next() again. Since there are no more yields to wait for we can see the saga is done.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c64dbba05935" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/testing-redux-sagas-c64dbba05935">Testing Redux Sagas</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hacked Tabletop TV Beta]]></title>
            <link>https://medium.com/hacked-tabletop/hacked-tabletop-tv-beta-7611dc4d5c77?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/7611dc4d5c77</guid>
            <category><![CDATA[twitch]]></category>
            <category><![CDATA[live]]></category>
            <category><![CDATA[extra]]></category>
            <category><![CDATA[live-streaming]]></category>
            <category><![CDATA[board-games]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Sat, 07 Apr 2018 15:38:29 GMT</pubDate>
            <atom:updated>2018-04-07T16:55:57.608Z</atom:updated>
            <content:encoded><![CDATA[<h4>Streaming Board Gaming Live on Twitch</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/859/1*swHs6cX7-1EfX1LXyO5nCg.png" /></figure><p>After many months and well over $4000 spent on gear and countless hours of trial and error, we are excited to premier the Hacked Tabletop TV on Twitch.</p><p>It’s going to be a glorious epic train wreck, the likes of which you <strong><em>will not </em></strong>want to miss.</p><p>The whole reason I started streaming on Twitch was to learn the ropes, build up a small following, get all the gear figured out, all for Hacked Tabletop TV. It’s has been not simple task, and way more of a time commitment then I expected. <a href="https://medium.com/@aaron.klaser/what-i-have-learned-after-2-weeks-of-streaming-on-twitch-6da84e342e29">I wrote and article about it a few weeks ago.</a> And let’s be clear, I am still no expert and still have a lot of work to get things set up how I want, but I think we are ready to start Hacker Tabletop TV!</p><h4>What is Hacked Tabletop TV?</h4><p>Honestly, we don’t know where this is gonna go, but we know where it will start.</p><p>Hacked Tabletop TV will be a weekly show where we talk about board games, reviews, opinions, jokes (often ruthlessly attacking each others intelligence), but most importantly, we are play board games… LIVE!</p><h4>I know what you thinking</h4><p>We have all seen the countless number of videos of people place board game on camera. Crappy camera, crappy lighting, and crappy sound. We plan to be different, maybe a little rough at first, but quality has always been my top priority which is part of the reason why it has taking this long to get started.</p><p>We will have soft box lights, an overhead DSLR camera so you can get the best view of the board, plus two webcams grabbing different angles of us, as we play. I have an Elgato Stream Deck that will allow me to change cameras on the fly, and trigger sound effects and animation pop-ups.</p><p>All this semi-professional stream quality is on top of the fact that we’re moderately entertaining people that love to joke and pick on each other and have a passion for all things gaming, both tabletop and digital.</p><blockquote><strong>Note:</strong> Some jokes and most language might be inappropriate and not suited for younger viewers.</blockquote><h4>When and Where can you watch Hacked Tabletop TV?</h4><p><strong><em>Thursday night at 7:30 </em></strong><em>on</em><strong><em> Twitch</em></strong><em> at </em><a href="https://www.twitch.tv/loserkidklazy"><em>https://www.twitch.tv/loserkidklazy</em></a></p><p>During these first few attempts, we will be streaming on my Twitch channel. We will eventually branch out to multiple networks to find what works best for our views. I like the idea of Mixer because it’s real-time and we can play games with our viewers but for now, Twitch will work.</p><h4>What are we playing?</h4><p>This first week we are playing our new favorite game, <strong>Dinosaur Island</strong>. You can see our review <a href="https://medium.com/hacked-tabletop/dinosaur-island-ddc095aac9c6">here</a>.</p><p>Other upcoming games include Gloomhaven, Spirit Island, Azul, Sagrada, and the Gaia Project.</p><p>If you have any game suggestions, or maybe you want us to play <em>your</em> game, feel free to reach out to us at <strong>hackedtabletop@gmail.com</strong>, or you can message us on <a href="https://www.facebook.com/Hacked-Tabletop-435495753539245/">facebook</a>.</p><h4>How can you help?</h4><p>Come hangout, create an account if you don’t already have one, <strong>Follow</strong> <a href="https://www.twitch.tv/loserkidklazy">LoserKidKlazy</a>, and chat with us live. We will all have our phones on and be available in chat. As an added bonus, you will gain “cool points” while you are watching us play games.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7611dc4d5c77" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hacked-tabletop/hacked-tabletop-tv-beta-7611dc4d5c77">Hacked Tabletop TV Beta</a> was originally published in <a href="https://medium.com/hacked-tabletop">Hacked Tabletop</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ultimate Audio Streaming Setup]]></title>
            <link>https://medium.com/hackernoon/ultimate-audio-streaming-setup-47340a0a157e?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/47340a0a157e</guid>
            <category><![CDATA[gaming]]></category>
            <category><![CDATA[xbox-one]]></category>
            <category><![CDATA[audio]]></category>
            <category><![CDATA[streaming]]></category>
            <category><![CDATA[twitch]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Thu, 15 Mar 2018 20:06:08 GMT</pubDate>
            <atom:updated>2018-03-15T21:26:35.785Z</atom:updated>
            <content:encoded><![CDATA[<h4>Twitch Streaming for Xbox and PC</h4><h4>Remove Spotify, include Xbox Party Chat, and use wireless PC headphones on Xbox One</h4><p>I have been streaming on twitch for a few night and I have ran onto and handful off issue when it comes to have auto like Spotify play in the background especially when I do my code streams on Wednesday night, because I want to record the videos to edit them later, but things like Spotify make cleanly editing impossible.</p><p>The other MAJOR issues is when a stream my Xbox, I need to be connected to both for audio and chat, so I can talk on Discord or Xbox Live, and hear my computer and my Xbox audio.</p><p>After <em>many</em> hour of research I came up with a pretty okay solution with our needing to buy an expensive mixer device and it lets me us my Yeti mic for Xbox Chat.</p><h3>Whats You need</h3><h4>Hardware</h4><p>PC with Windows 10 (Alienware R7)<br>Xbox One<br>Microphone (ex. Blue Yeti)<br>Elgato HD60(S)<br>Wireless Headphones (Arctis 7)</p><h4>Software</h4><p>OBS (or SLOBS)<br>Voicemeeter Banana<br>Virtual Audio Cable (With the Extra Cables A &amp; B — $5)<br>Audio Router<br>Spotify<br>Discord<br>Windows 10 Xbox App</p><h3>The Diagram</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_vghzUD4JcEDO699MFTCag.jpeg" /></figure><h3>How To Set It All Up</h3><p><strong>Step 1</strong>: Download and in all Install all the application<br><a href="https://obsproject.com/download">OBS</a><br><a href="https://www.vb-audio.com/Voicemeeter/banana.htm">Voicemeeter Banana</a><br><a href="https://www.vb-audio.com/Cable/index.htm">Virtual Audio Cable (With the Extra Cables A &amp; B — $5)</a><br><a href="https://github.com/audiorouterdev/audio-router">Audio Router</a><br><a href="https://www.spotify.com/us/download/other/">Spotify</a><br><a href="https://discordapp.com/download">Discord</a></p><p><strong>Step 2</strong>: Set up Spotify, Discord, and OBS. This one you can handle on your own.</p><p><strong>Step 3</strong>: Install all three Virtual Cable Drivers. Then, restart you computer so all 3 driver will work. If you open your playback devices (right click on volume and click playback) You should see, rather counter intuitively, Playback now has three CABLE Inputs and Recording has three CABLE Outputs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/401/1*VpiNksSbjIOPFyGgOUNavA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/403/1*2IZBEALC2KNRlUs2n4VW-A.png" /></figure><p><strong>Step 4</strong>: Set <strong>CABLE Input</strong> (Not A or B) to be the default playback device for BOTH <strong>Default Device</strong> and <strong>Default Communications Device</strong>. Right click on CALBE Input and make check Set as Default Device and Set as Default Communications Device. If the are both set there will be no options under Disable.</p><p><strong>Step 5</strong>: Install (well… run) <strong>Audio Router</strong>. Find <strong>Spotify</strong> in your list, it will need audio to show up in the mix, or you click <strong>File &gt; Switch View</strong> and select it from the list. Then choose Route… and pick <strong>CABLE-A Input</strong></p><blockquote><strong>Note:</strong> This app is in crazy alpha mode and for some reason does have yet so for now you have to do this step every time open Audio Router.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4EE-_bqeYwinoYsnsQ-_vg.png" /></figure><p><strong>Step 6:</strong> Open<strong> Voicemeeter Banana</strong> and set the follow mappings then save so you can load again later.<br>Hardware Input 1 =&gt; CABLE Output<br>Hardware Input 1 =&gt; CABLE-A Output<br>Hardware Input 1 =&gt; Line (Elgato Sound Capture) or CABLE-B Output if your not stream a console.<br>Hardware Out =&gt; Set A1 to Headphones (Arctis 7)</p><blockquote><strong>IMPORTANT: </strong>Make sure each A1 on each input is select otherwise the sound wont go to your headphones on the A1 output.<br><strong>EVEN MORE IMPORTANT:</strong> After making any changes to the<strong> </strong>Voicemeeter Banana or Audio Router, you almost always have to restart the audio engine in Voicemeeter Banana by clicking <strong>Menu &gt; Restart Audio Engine</strong></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1019/1*zv3qAC6wPlo1sDr4tpYVwA.png" /></figure><p><strong>Step 7</strong>: Open your OBS scene: <br>Add new Video Capture Device name it Elgato and select Device as <strong>Game Capture HD60 S<br></strong>Add new Audio Input Capture name it mic and select Device as <strong>Microphone (Blue Yeti)<br></strong>Add new Audio Output Capture name it spotify and select Device as <strong>CABLE-A Input</strong></p><p>Then, click the gear the on Spotify mixer &gt; Advance Audio Properties. For Spotify over under Tracks uncheck all but #1</p><p><strong>Step 8 (Option for Party Chat)</strong>: Use the Windows Xbox App to join Party Chat on your PC, Not on the Xbox. It works exactly the same way.</p><p>There you go. Now you can stream live with background music but not have it include in your recording so you can edit. This is also copyright friendly :)</p><h4>What you can’t do</h4><p>Talk in the general online chat on Xbox Live games like Call of Duty. But to be honest, no one wants to hear you talk especially if your voicing your inner monologue on Twitch. If people wanted to talk you, you can use Party Chat on the Xbox App, then your views can hear them and your not annoying the crap out of people too stupid to mute you in-game.</p><h3>Video Explanation</h3><blockquote><strong>IMPORTANT NOTE:</strong> I meant to record this but accidentally Streamed it so you can hear audio that you shouldn’t hear but I promise it works. I will make a follow-up video with every running live in the next week!</blockquote><p>Don’t forget to <strong>follow </strong>and <strong>watch me live </strong>at <a href="https://www.twitch.tv/loserkidklazy">https://www.twitch.tv/loserkidklazy</a></p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FoKeK-rgnhyo%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DoKeK-rgnhyo&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FoKeK-rgnhyo%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/7e2691c6003e0d0b129c1d4fa0d85469/href">https://medium.com/media/7e2691c6003e0d0b129c1d4fa0d85469/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=47340a0a157e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/ultimate-audio-streaming-setup-47340a0a157e">Ultimate Audio Streaming Setup</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What I Have Learned After 2 Weeks of Streaming on Twitch]]></title>
            <link>https://medium.com/@aaron.klaser/what-i-have-learned-after-2-weeks-of-streaming-on-twitch-6da84e342e29?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/6da84e342e29</guid>
            <category><![CDATA[streaming]]></category>
            <category><![CDATA[gaming]]></category>
            <category><![CDATA[twitch]]></category>
            <category><![CDATA[online]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Fri, 09 Mar 2018 19:13:33 GMT</pubDate>
            <atom:updated>2018-03-10T04:45:36.957Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LRjBsUgS1XYpjVmhkpddHg.png" /></figure><p>For the last ten years, I have spent all my free time trying to start my own companies or build something new and innovative. But for the has few months I have shifted my focus to giving back and trying to create a brand and reputation for myself in the JavaScript community. It started with Medium, and then a board game review blog and the most recently, live streaming on Twitch.</p><blockquote><strong><em>Shameless Plug</em></strong><em><br>Wednesday nights (9:30cst) I stream javascript programming. If you want to learn some code tricks while being slight entertained follow me, I could use the followers. </em><a href="https://www.twitch.tv/loserkidklazy"><em>LoserKidKlazy</em></a><em><br>Sunday through Tuesday I stream games like the Witcher 3 and My Time at Portia. The rest of the week I will hop on an off randomly if I have the time.</em></blockquote><p>Heres what I have learned after 2 weeks of streaming.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OIEikqPUwTLF9VSoxXD3yg@2x.jpeg" /></figure><h4>First and most important</h4><p>Your significant other will not be happy with this. They might try to play it cool, but for some reason as soon as you turn on the camera they want your attention, and then they are mad at you for days about it. Just know this before you start, and to my wife, if you are reading this, I’m sorry, and I love you :P</p><h4>It’s a lot of work</h4><p>It’s far more work than I actually expected. Learning how to use the Gear, getting everything setup, building a brand, researching how other people do things, figuring out what tools to use and how they work. It’s a lot of effort to and research, and trial and error. Yes, you could just start broadcasting yourself playing games but if you’re want to be even mildly successfully expect to devote a lot of time to getting started.</p><h4>It’s expensive</h4><p>This is no joke. Sure you could stream on a mediocre PC, but it’s not going to be very good. I was going to use my Mac Book Pro at first and an Elgato HD60s to stream my Xbox, but my Mac wasn’t strong enough. So I had to buy a PC, and a better webcam, and desk, and gaming headset, and keyboard, and a mouse, and a monitor… you get the point. Some of you might have a lot these things laying around, but they do need to be good if you want to look professional. I’ll list my gear and costs to date below.</p><h4>It takes awhile to get going</h4><p>Like I stated up above, there is a lot more to it than just turning on your computer and streaming. The learning curve is a little on the steep side at first. I have been trying to start streaming for about 2 months, but it’s taken about 6 weeks to get everything set up in a way they I can stream with any form of consistency.</p><h4>Scheduling and consistency is important</h4><p>Everyone will tell you this, and it makes sense. You need to be consistent, and a have a schedule and try to stick with it. You want your follower to know when they can watch you, and maybe even some will be excited and looking forward. Don’t let them down.</p><h4>Its weird, but important, to talk to yourself</h4><p>I’m still getting used to this. Most of the time you won’t have anyone in your room, and even when you do most people still won’t be talking to you. It’s not a live chat; you are streaming to produces entertainment, be entertaining. In fact, Twitch has a 10-second delay so round trip you could be off by 20 seconds. You have 15 seconds to impress a lurker. You need to be exciting to watch even when no one is watching unless you’re are insanely good at a particular game, and even then.</p><h4>If you are going to code be sure to zoom in</h4><p>My first code stream was terrible, you can see it on my youtube page, or better put, you can’t because my 1440p monitor streamed at 720p was so tiny you can barely make out what I’m coding.</p><h4>Pay attention to your audio</h4><p>My first code stream I also forgot to turn my audio on for the first 8 minutes. Oops. And if you have your Twitch stream up for chat or something, be sure to mute it because it will for some weird feedback loop.</p><h4>Don’t stream games that are too popular, or not popular enough</h4><p>If it’s a popular game, all the big guys are going to have all the viewers. If it’s not popular enough, no one is ever going to find you. You can use a site like Twitch Strike to see that you should be playing. Then find something YOU WANT to play.</p><h4>Build your brand</h4><p>There are few ways to do this. You can buy something if you are less artistically inclined, or you can build your own. I’m working on building my own brand right now, and again this adds to even more time spent on setup and research.</p><h4>You can’t make money unless you get followers</h4><p>Once you have 50 followers and meet a few other simple goals, you can become a Twitch Affiliate, which lets you get subscribers, which they pay $5 a month to support you and you get some kind of cut of that $5. This can add up pretty quick. Some of these big times followers make upwards of 100s of thousands of dollars a month. Granted they have been doing for YEARS. I don’t get, but It’s a thing. There is a Twitch Partner level as well. Not sure what the difference is, I just know I’m lightyears away from it.</p><h4>Set a Discord Server</h4><p>Everyone uses it’s for live chatting and offline chat rooms. It’s like Slack but better and more confusing.</p><h4>Good Camera and Good Lighting</h4><p>Light makes a HUGE difference, especially if you want to do a green screen. Get yourself two softboxs off amazon and set them up kind of in front of you and to the left and right of your desk. I had to use a boom arm to get mine in for of me on the right side because my desk is in the corner of my room. If you have a green screen be sure to set up a hair light to cast light on the back of your head, this will reduce the green screen bleed around your image.</p><h4>Streamlabs and Chat Bots</h4><p>I’m still trying to figure our the Chatbot, but using Streamlabs and Chat Bots is how you get some interactivity with your stream. If you want to have a pop-up and some audio every time someone follows you, this is how you do it. But, expect to spend a few days playing with it to figure out how it works and what works for your brand.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*oZH189O_Y6wGF7uHj0MSpQ@2x.jpeg" /></figure><p>That’s about it for now. I am by no means an expert, but these are just a few of the thing I have learned over the last few weeks. If you’re interested in getting started, I hope this helps, and I wish you the best of luck. If you want to help support me, you can follow me on Twitch, Facebook, and Twitter at these links:</p><p>Twitch — <a href="https://www.twitch.tv/loserkidklazy">https://www.twitch.tv/loserkidklazy</a><br>Facebook — <a href="https://www.facebook.com/LoserKidKlazy/">https://www.facebook.com/LoserKidKlazy/</a><br>Twitter — <a href="https://twitter.com/awklaser">https://twitter.com/awklaser</a><br>YouTube — <a href="https://www.youtube.com/channel/UCLND4ARfpDGvLpeS9T8LeXg?view_as=subscriber">https://www.youtube.com/channel/UCLND4ARfpDGvLpeS9T8LeXg?view_as=subscriber</a></p><h4>As promised my gear list.</h4><p>This is just what I have purchased in the last two months for doing this.</p><p><strong>PC</strong> — Alienware Aurora R7 = $2000<br>CPU — Intel i7 8700<br>GPU — Geforce GTX 1080<br>RAM<strong> </strong>— HyperX Fury 16GB<br><strong>Monitor </strong>— Dell 27&quot; 1440p 144hz Gsync = $430 <br><strong>Capture</strong> — El Gato HD60 = $130<br><strong>Webcam</strong> — Logitech C922 = $99<br><strong>Mic</strong> — Red Yeti Mic = $160<br><strong>Headset</strong> — Steelseries Arctics 7 = $150<br><strong>Keyboard</strong> — Ducky Shine 6 — $150<br><strong>Mouse</strong> — Logitech G602 — $40<br>Controller — Xbox Pro Controller / PS4</p><p><strong>Desk</strong> — IKEA concoction = $270<br><strong>Softbox Lighting</strong> and Green Screen = $150<br><strong>Adobe CC</strong> — $30/month<br><strong>Games</strong> — $100/month</p><p>Grand Total = ~$3,839</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LSG4byA9tJ_CiLlkbwQQAQ@2x.jpeg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6da84e342e29" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[People Are Dumb and Should be Treated Like Morons]]></title>
            <link>https://medium.com/hackernoon/people-are-dumb-and-should-be-treated-like-morons-78cb6e5051d0?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/78cb6e5051d0</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[user-experience]]></category>
            <category><![CDATA[ux]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Tue, 06 Mar 2018 17:51:57 GMT</pubDate>
            <atom:updated>2018-03-07T05:31:13.442Z</atom:updated>
            <content:encoded><![CDATA[<h4>A Guide to Good User Experience Design</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0vYpx6YZrlWCr2pVLsFtSQ.png" /></figure><p>In my ten years of web development and web design, I see people make the same stupid mistakes over and over when it comes to designing websites. Most people think that just because they understand something, that everyone understands it same way they do. But this never the case. EVER!</p><blockquote><strong><em>Important:</em></strong><em><br>Someone, somewhere, is not going to understand something the same way as you understand it, no matter how simple or intuitive something may seem to you, it will not make sense to someone else, and there is nothing any of us can do about it. You have to accept this, understand it, and move on knowing this.</em></blockquote><p><strong>I want you to read that again.</strong> It is critical that you understand what I just said to be successful in pretty much anything you do, but most importantly design and user experience. These people are the lowest common denominator. Which means, just like adding fractions we need to break it down before we add it all together.</p><p>I have lived by <strong>one simple rule,</strong> and it as made be very successful as a web developer, a designer, and pretty much everything else I’ve done.</p><blockquote>“People are dumb and should be treated like morons”</blockquote><p>If you don’t want a user to think something, you better clarify, because someone is going to think it. If you don’t want a user to click something, you better hide it or disable, because some moron is going to click it no matter what it says it does.</p><p><strong>Keep it simple, keep it stupid!</strong></p><p>Just because something makes sense to you doesn’t mean it makes sense to everyone else, and it is better to error on the side of <strong>over clarification</strong> than leaving important gaps because you assumed everyone understood something.</p><p>Think about, for instance, reading the rules to a board game, and they tell you that you can spend the red tokens as health. But, as you look at everything in the game you realize that there a 3 different red tokens and the rules never clarify which red tokens to use. It’s probably the ones with the cross on it, but who knows! I didn’t create the game.</p><p>This kind of crap happens all the time, everywhere! And if people would just get out of their own heads for a minute, stop making assumptions based on logic we could solve a lot of problems. Most people, even the smart ones can lack the ability to think logically, if not all the times, then at least sometimes.</p><p>It is our jobs as developers and user experience designers to try to make things so simple, so intuitive, that anyone, of any age, of any intelligence can figure it out. Just ask yourself, <strong>would my Grandma understand this</strong>. And still even then.</p><p>I have worked with a lot of people over the years that struggle with this concept, and the crappy part is, these are usually the people that don’t think the same way as everyone else. I often refer to this as the <strong>Curse of Intelligence</strong>, when someone is so smart they are stubborn and overly logical to a fault. Anyone that has worked in IT for over a year knows who I’m talking about, and if you don’t there is a good chance it’s you, case and point, so keep reading.</p><blockquote>“When I think of design and creating great user experiences, I generally think of it in terms of three things: usability, utility and desirability.” —<strong> Jon Wiley, Google</strong></blockquote><h3>10 User Interface Design Guidelines</h3><h4><strong>Visibility of system status</strong>.</h4><p>Users should always be informed of system operations with easy to understand and highly visible status displayed on the screen within a reasonable amount of time.</p><p><strong>Example:</strong> If a page, or a section on a page, is loading data, show the user a loading icon. Don’t wait for your page to load before taking the user to that page. This creates delays and makes the user feel like the site is broken. People are impatient and need instant feedback.</p><h4><strong>Match between system and the real world</strong>.</h4><p>Designers should endeavor to mirror the language and concepts users would find in the real world based on who their target users are. Presenting information in logical order and piggybacking on user’s expectations derived from their real-world experiences will reduce cognitive strain and make systems easier to use.</p><p><strong>Example</strong>: You wouldn’t make your user fill out his credit card information <em>before</em> shopping on your online store. You also probably aren’t going to make their cart their default homepage. Because thats not how people shop. You go to a store, look at products, fill your cart, then check out. Your users experiences should mirror that.</p><h4><strong>User control and freedom</strong>.</h4><p>Offer users a digital space where backward steps are possible, including undoing and redoing previous actions.</p><p><strong>Example: </strong>My god, we have all used these sites before. You get halfway through filling out some forms and you realize you $#@!ed up. To bad, can go back, got to start all over. Yeah… Don’t do that.</p><h4><strong>Consistency and standards</strong>.</h4><p>Interface designers should ensure that both the graphic elements and terminology are maintained across similar platforms. For example, an icon that represents one category or concept should not represent a different concept when used on a different screen.</p><p><strong>Example:</strong> Holy crap, this is probably the most abused rule. Learn More, Read More, Read, View More, View, More… PICK ONE!!! They are all the same thing really! Close, Cancel, Finished, Done, X… Yes, sometimes it makes sense to have different ones like and Close and Cancel. Just stay consistent with <em>how</em> you use Close and Cancel. And, if close is a red button with an X one page it better not be a green button with check on another page.</p><h4><strong>Error prevention</strong>.</h4><p>Whenever possible, design systems so that potential errors are kept to a minimum. Users do not like being called upon to detect and remedy problems, which may on occasion be beyond their level of expertise. Eliminating or flagging actions that may result in errors are two possible means of achieving error prevention.</p><p><strong>Example: </strong>Try not to show users errors unless there is a way that the user can fix it, and make that way clear and simple. If your user is filling out a form, and certain data is requires or must be in a specific format, don’t let them submit the form without first filling out that data. If your user doesn’t have access to a certain page or action, hide it so they don’t receive an error when they click on it or try to visit that page.</p><h4><strong>Recognition rather than recall.</strong></h4><p>Minimize <a href="https://www.interaction-design.org/literature/topics/cognitive-load">cognitive load</a> by maintaining task-relevant information within the display while users explore the interface. Human attention is limited and we are only capable of maintaining around five items in our short-term memory at one time. Due to the limitations of short-term memory, designers should ensure users can simply employ recognition instead of recalling information across parts of the dialogue. Recognizing something is always easier than recall because recognition involves perceiving cues that help us reach into our vast memory and allowing relevant information to surface. For example, we often find the format of multiple choice questions easier than short answer questions on a <a href="https://www.interaction-design.org/literature/topics/test">test</a> because it only requires us to recognize the answer rather than recall it from our memory.</p><p><strong>Example: </strong>Say you want to have a short survey after users checkout from your online store, while be it annoying no one is ever actually going to fill it out. Instead of requiring your users to describe their experience in text box, present them with multiple choices like; Great, Good, Poopy, Horrific, and then give them the <strong><em>option</em></strong> to express why they felt that way, even though they will never fill it out. Also helpful, if your survey has multiple pages, and you want to reference something from a previous question, include some blurb about that question and their answer on that page the user is currently on. That way the user knows what you’re asking them… even though that are never going to fill it out (-_-)</p><h4><strong>Flexibility and efficiency of use</strong>.</h4><p>With increased use comes the demand for less interactions that allow faster <a href="https://www.interaction-design.org/literature/topics/navigation-1">navigation</a>. This can be achieved by using abbreviations, function keys, hidden commands and macro facilities. Users should be able to customize or tailor the interface to suit their needs so that frequent actions can be achieved through more convenient means.</p><p><strong>Example: </strong>Medium has a great example of hidden commands. If you are writing an article in Medium and you press [Command]+[ / ] it will open the Keyboard Shortcuts menu on in your browser. Its not customizable a but it’s really easy to get to and find a helpful shortcut key.</p><h4><strong>Aesthetic and minimalist design</strong>.</h4><p>Keep clutter to a minimum. All unnecessary information competes for the user’s limited attentional resources, which could inhibit user’s memory retrieval of relevant information. Therefore, the display must be reduced to only the necessary components for the current tasks, whilst providing clearly visible and unambiguous means of navigating to other content.</p><p><strong>Example: </strong>Cut the annoying intrusive ads that are littered all over your web pages. I know, that’s how you make money but mother of god, does anyone actually click on those things on purpose. Medium is another great example of clean minimal user interface. Nothing to distract the users from the stories being told. Sometimes less is more.</p><h4><strong>Help users recognize, diagnose and recover from errors</strong>.</h4><p>Designers should assume users are unable to understand technical terminology, therefore, error messages should almost always be expressed in plain language to ensure nothing gets lost in translation.</p><p><strong>Example: </strong>I<strong> </strong>have already hit on this point but the bigger take away here is to speak in plain english — or what ever native language your site is in. An error <strong>shouldn’t</strong> read something like “Our servers were unable to authenticate your identity token”, it <strong>should </strong>just be<strong> </strong>“Login Failed”… because, that’s what it is. Keep it simple, keep it stupid. Remember, people are dumb, even if your site is something like technical documentation for debugging a supercomputer and the only people ever to read it are computer scientists. Don’t assume they know what you’re talking about, because like I said before, someone won’t understand.</p><h4><strong>Help and documentation</strong>.</h4><p>Ideally, we want users to navigate the system without having to resort to documentation. However, depending on the type of solution, documentation may be necessary. When users require help, ensure it is easily located, specific to the task at hand and worded in a way that will guide them through the necessary steps towards a solution to the issue they are facing.</p><p><strong>Example: </strong>Sometimes shits complicated, and in those instances we might need to help the user. If you can, give them a tooltip pop-up. If it needs more explanation then a tooltip can provide, then create some sort of documentation page. But above all make both tips and/or the documentation <strong>follow all of the above rules</strong>.</p><h3>Final Thought</h3><p>A users experience shouldn’t be like an episode of Lost where the user is left with more $#@!ing questions and no $#@!ing answers.</p><p>Assume people have no clue what you are talking about no matter how obvious something actually is.</p><p>Remember to think to yourself, will my grandma understand this.</p><p><a href="https://www.interaction-design.org/literature/article/user-interface-design-guidelines-10-rules-of-thumb"><em>Source</em></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=78cb6e5051d0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/people-are-dumb-and-should-be-treated-like-morons-78cb6e5051d0">People Are Dumb and Should be Treated Like Morons</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Replacing Redux Thunks with Redux Sagas]]></title>
            <link>https://medium.com/hackernoon/replacing-redux-thunks-with-redux-sagas-4aa306854925?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/4aa306854925</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[redux]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[redux-saga]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Tue, 27 Feb 2018 20:31:57 GMT</pubDate>
            <atom:updated>2018-02-28T15:32:44.026Z</atom:updated>
            <content:encoded><![CDATA[<h4>The React Blog Series: Part Five</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*-GC9G3YWGrTD85yV5N0STg.png" /></figure><p>This article is part five in a weekly series on building a blog with React and is an extension of the code created in previous parts.</p><h4>The React Blog Series</h4><p><a href="https://medium.com/front-end-hacking/building-a-website-with-react-and-bulma-d655214bff2a"><strong>Part One: </strong>Building a website with React and Bulma</a><br><a href="https://medium.com/@aaron.klaser/building-a-blog-with-react-and-contentful-fd538f68f6fb"><strong>Part Two:</strong> Building a Blog with React and Contentful</a><br><a href="https://medium.com/@aaron.klaser/import-your-medium-feed-into-react-ceadbaf785c7"><strong>Part Three:</strong> Import your Medium Feed into React</a><br><a href="https://medium.com/@aaron.klaser/adding-redux-to-a-react-blog-97f5fea606c2"><strong>Part Four:</strong> Adding a Redux to a React Blog</a><br><strong>Part Five:</strong> Replacing Redux Thunks with Redux Sagas<br><strong>Part Six:</strong> (<em>In Progress</em>) Writing Unit Test for a React Blog with Redux Sagas</p><blockquote>First of all, let me start with <strong>I am by no means an expert</strong> on this subject. I have only actually been using Sagas for the last two weeks. If you’re anything like me, as you read this, you will constantly be asking yourself, why the hell would anyone want to use this, it’s massive overkill, and you would be absolutely correct. It is overkill… for what I’m doing, but at the end of the day Sagas are better, more scalable, and infinitely easier to unit test which we will see in Part Six. If you don’t like my explanations or examples, I understand. There is also SO MUCH more to Sagas than what I’m going to use here. <a href="https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html">Here is the link to the Redux Saga documentation.</a> Good Luck! — <a href="https://medium.com/u/1ef30430402c">Aaron Klaser</a></blockquote><h3>What is Redux Saga?</h3><p>Sagas are a redux middleware that handles <em>asynchronous actions </em>triggered in your application often referred to as ‘<strong>side effects’</strong>. They are constantly watching for certain actions to be dispatched, before they but in and take over controller of the situation.</p><p>Sagas are implemented as <strong>Generator functions</strong> that <em>yield</em> objects to the redux-saga middleware.</p><h4>Wait, what is a Generator function?</h4><p>!@#$ing magic, thats what!</p><p>But, heres a better explanation which I stole from <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*"><strong>Mozilla</strong></a>.</p><p>Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.</p><p>Calling a generator function does not execute its body immediately; an <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterator">iterator</a> object for the function is returned instead. When the iterator’s next() method is called, the generator function&#39;s body is executed until the first <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield">yield</a> expression, which specifies the value to be returned from the iterator or, with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield*">yield*</a>, delegates to another generator function. The next() method returns an object with a value property containing the yielded value and a done property which indicates whether the generator has yielded its last value as a boolean. Calling the next() method with an argument will resume the generator function execution, replacing the yield expression where execution was paused with the argument from next().</p><p>A return statement in a generator, when executed, will make the generator done. If a value is returned, it will be passed back as the value. A generator which has returned will not yield any more values.</p><h4>Okay, Back to Sagas</h4><p>Now where was I? Ah yes… Sagas are implemented as Generator functions that <em>yield</em> objects to the redux-saga middleware. The yielded objects are a kind of instruction to be interpreted by the middleware. When a Promise is yielded to the middleware, the middleware will suspend the Saga until the Promise completes. Once the Promise is resolved, the middleware will resume the Saga, executing code until the next yield.</p><p>Redux Saga middleware does a lot of the Generator magic for us behind the scenes yielding to saga functions like put, all, call, and takeLatest.</p><p>What happens is that the middleware examines the type of each yielded Effect then decides how to fulfill that Effect. If the Effect type is a PUT then it will dispatch an action to the Store. If the Effect is a CALLthen it&#39;ll call the given function. This separation between Effect creation and Effect execution makes it possible to test our Generator in a surprisingly easy way using next()</p><blockquote>In short, <strong>Generators</strong> are pause able functions like a music track, and <br><strong>Sagas</strong> are the DJ that press the pause play and stop buttons on the track.</blockquote><p>But enough chit chat, let’s get started!</p><h3>Install Saga</h3><pre>npm install redux-saga</pre><p>Then in store/index.js let’s connect Saga. Saga is a redux middleware so we will be injected it into our redux applyMuiddleware function, but we will have to create it first.</p><pre>import { createStore, applyMiddleware } from &#39;redux&#39;<br>import { rootReducer } from &#39;./rootReducer&#39;<br>import reduxImmutableStateInvariant from &#39;redux-immutable-state-invariant&#39;<br>import thunk from &#39;redux-thunk&#39;<br>import { composeWithDevTools } from &#39;redux-devtools-extension&#39;;<br><strong>import createSagaMiddleware from &#39;redux-saga&#39;</strong></pre><pre><strong>const sagaMiddleware = createSagaMiddleware()</strong></pre><pre>export function configureStore() {<br>  return createStore(<br>    rootReducer,<br>    composeWithDevTools(<br>      applyMiddleware(<br>        <strong>sagaMiddleware,</strong><br>        thunk,<br>        reduxImmutableStateInvariant()<br>      )<br>    )<br>  )<br>}</pre><p>As of right now, this does nothing. We will need to initialize the sagas which need some set up before we can do that. We are going to basically gut our current thunk setups.</p><h3>Creating Our Saga Helpers</h3><p>We are going to use three helpers:</p><ol><li><strong>createAsyncTypes</strong> helper, which will generate a type object with pending, success, and error.</li><li><strong>createActions </strong>helper, which will generate our actions for us.</li><li><strong>createReducer</strong> helper which we will use to replace our switch statements.</li></ol><p>In the store folder create a file called <strong>Utilities.js</strong></p><pre><em>///////////////////<br>// ActionHelpers //<br>///////////////////<br></em>const asyncTypes = {<br>  PENDING: &#39;PENDING&#39;,<br>  SUCCESS: &#39;SUCCESS&#39;,<br>  ERROR: &#39;ERROR&#39;<br>}</pre><pre>export const <strong>createAsyncTypes</strong> = typeString =&gt;<br>  Object.values(asyncTypes).reduce((acc, curr) =&gt; {<br>    acc[curr] = `${typeString}_${curr}`<br>    return acc<br>  }, {})</pre><pre>export const <strong>createAction</strong> = <br>  (type, payload = {}) =&gt; <br>    ({ type, ...payload })</pre><pre><em>///////////////////<br>// createReducer //<br>///////////////////<br></em>export const <strong>createReducer</strong> = <br>  (initialState, handlers) =&gt;<br>    (state = initialState, action) =&gt;<br>      handlers.hasOwnProperty(action.type)<br>      ? handlers[action.type](state, action)<br>      : state</pre><p>I might revisit these in the future, I’m not 100% sold on the <strong>createReducers</strong> approach yet, but for now it works and it’s pretty clean</p><h3>Moving from Thunks to Sagas</h3><h4>Update Blog Types</h4><p>Open store/blog/types.js</p><p>Before, we only had 2 types, we used BLOG_LOADING to set the loading state for both complete and error, and LOAD_BLOG_SUCCESS to load our data on success.</p><pre><strong>--- BEFORE ---</strong></pre><pre><em>/**</em><br><em>* Blog Types</em><br><em>*/</em><br>export const BLOG_LOADING = &#39;BLOG_LOADING&#39;<br>export const LOAD_BLOG_SUCCESS = &#39;LOAD_BLOG_SUCCESS&#39;</pre><p>But now, we are going to call our new help to createAsyncTypes which will create our three types automatically; Pending (loading ), Success (load data), Error (loading complete).</p><pre><strong>--- After ---</strong></pre><pre><em>/**</em><br><em>* Blog Types</em><br><em>*/<br></em><strong>import</strong> { createAsyncTypes } from &#39;./../Utilities&#39;<br><br><em>//Using ASYNC as a convention to know that I&#39;ll have three types.</em><br><strong>export</strong> <strong>const</strong> GET_BLOG_ASYNC <strong>=</strong> createAsyncTypes(&#39;GET_BLOG&#39;)</pre><p>This will make more sence in a moment.</p><h4>Update Blog Reducer</h4><p>Open store\blog\reducer.js</p><p>This can be done one of two ways, we could just simply update the types in the switch cases to use the new type names we just created like this.</p><pre>switch (action.type) {<br>  case types.<strong>GET_BLOG_ASYNC.PENDING</strong>:<br>    return {<br>      ...state,<br>      loading: true<br>    }<br>  case types.<strong>GET_BLOG_ASYNC.SUCCESS</strong>:<br>    return {<br>      ...state,<br>      posts: action.posts,<br>      loading: false<br>    }<br>  case types.<strong>GET_BLOG_ASYNC.ERROR</strong>:<br>    return {<br>      ...state,<br>      loading: false<br>    }<br>  default:<br>    return state<br>}</pre><p>This is an example of what our <strong>createAsyncTypes</strong> types helper has done. It generated a type object with 3 values and using the convention _ASYNC we know that <strong>GET_BLOG_ASYNC</strong> will<strong> </strong>contains PENDING, SUCCESS, and ERROR.</p><p>Let’s make this a little bit more intelligent by using out createReducer helper and passing it an object instead of a switch statement.</p><p>Delete everything and paste in this</p><pre><em>/**<br>* Blog Reducer<br>*/<br></em>import initialState from &#39;./../initialState&#39;<br>import { createReducer } from &#39;./../Utilities&#39;<br>import * as types from &#39;./types&#39;</pre><pre>export default createReducer(initialState.blog, {<br>  [types.GET_BLOG_ASYNC.PENDING](state) {<br>    return {<br>      ...state,<br>      loading: true<br>    }<br>  },<br>  [types.GET_BLOG_ASYNC.SUCCESS](state, action) {<br>    return {<br>      ...state,<br>      posts: action.posts,<br>      loading: false<br>    }<br>  },<br>  [types.GET_BLOG_ASYNC.ERROR](state) {<br>    return {<br>      ...state,<br>      loading: false<br>    }<br>  }<br>})</pre><p>Yes, I know what your thinking and I agree. This does look more confusing then a switch statement and just as clean. It actually took me a minute to understand what that object was actually doing because it wasn’t something I had ever written out like this we before. It’s creating an object that each equals and an anonymous functions but it’s setting the keys using square brackets.</p><pre>obj = {<br>  key: fn,<br>  [key]fn<br>}</pre><p>Or you can use arrow functions which you will need to a colon arrow and parentheses around the object.</p><pre>export default createReducer(initialState.blog, {<br>  [types.GET_BLOG_ASYNC.PENDING]<strong>:</strong>(state)<strong> =&gt; (</strong>{<br>    ...state,<br>    loading: true<br>  }<strong>)</strong>,<br>  [types.GET_BLOG_ASYNC.SUCCESS]:(state, action) =&gt; ({<br>    ...state,<br>    posts: action.posts,<br>    loading: false<br>  }),<br>  [types.GET_BLOG_ASYNC.ERROR]:(state) =&gt; ({<br>    ...state,<br>    loading: false<br>  })<br>})</pre><p>Which ever way you choice is fine, let I said I’m not 100% on board <em>yet</em>, but I think this can help create an async reducer creator in the future ;)</p><h4>Create Our First Saga</h4><p>In our store\blog folder create a file called <strong>sagas.js</strong></p><pre><em>/**<br>* Blog Sagas<br>*/<br></em>import * as contentful from &#39;contentful&#39;<br>import { all, call, put, takeLatest } from &#39;redux-saga/effects&#39;<br>import { actions } from &#39;./../Blog&#39;<br>import * as types from &#39;./types&#39;</pre><pre>const client = contentful.createClient({<br>  space: &#39;qu10m4oq2u62&#39;,<br>  accessToken: &#39;f4a9f68de290d53552b107eb503f3a073bc4c632f5bdd50efacc61498a0c592a&#39;<br>})</pre><pre>const fetchPosts = () =&gt; client.getEntries()</pre><pre>function* getBlogPosts() {<br>  try {<br>    const posts = yield call(fetchPosts)<br>    yield put(actions.success(posts.items))<br>  } catch (e) {<br>    console.log(e)<br>    yield put(actions.error(e))<br>  }<br>}</pre><pre>export default function* () {<br>  yield all([<br>    takeLatest(types.GET_BLOG_ASYNC.PENDING, getBlogposts)<br>  ])<br>}</pre><p>So, what is this doing? Well, let’s start with the obvious.</p><p><strong>client</strong> is my connection to my Contentful</p><p><strong>fetchPosts</strong> calls our to Contentful to retrieve my posts and returns them as a Promise</p><p><strong>getBlogPosts</strong> is our actual saga. It first yields the call to fetchPosts, saga magic kicks in and pauses the function and waits for Contentful to return the data. Then, once the data has been successfully returned, instead of returning a promise it actually return the data to const posts and calls next() behind the scenes which tells the function to play again. It then calls yield put which magically dispatches the the actions with the post items. If fetchPosts() where to fail, an exception is thrown which triggers the catch.</p><p><strong>export default function*</strong> is the action watcher that is connected to the Redux Saga middleware. This is similar to trigger the actions, except instead of the call an action and the action being dispatched to the reducer, the saga watches for that actions to be called, then it intercepts and calls the saga. takeLatest tells Redux Saga to stop any previous running saga task if still running then run a new one.</p><p>takeLatest(types.GET_BLOG_ASYNC.PENDING, getBlogPosts) says when the action <strong>types.GET_BLOG_ASYNC.PENDING </strong>is called, stop any getBlogPosts tasks previously call that my be not be completed and call a new <strong>getBlogPosts()</strong> function.</p><p>The yield all function is a kind of housing to hold all of our watchers for the blog sagas. If we have more actions, we could create more sagas and create more watchers to call those sagas, those additional watchers would go in side the all function.</p><p>We then export the default function, which creates gets injected into bound to the sagaMiddleware.run function in the rootSaga.</p><h4>Setting Up the rootSaga</h4><p>We need to add all our sagas to the sagaMiddleware so we can start all of our watchers when the app loads.</p><p>In the store folder, create a file called <strong>rootSaga.js</strong></p><pre>import blog from &#39;./blog/sagas&#39;</pre><pre>const sagas = [<br>  blog<br>]</pre><pre>export const initSagas = (sagaMiddleware) =&gt;<br>  sagas.forEach(sagaMiddleware.run.bind(sagaMiddleware))</pre><p>Right now, we only have the one saga. If we set up the Medium store with Saga’s then we would import medium and pass it in to the sagas array.</p><blockquote><strong>Note:</strong> we don’t need to import every function from our <strong>blog/saga.js </strong>file, only t action watchers, which we wrapped in an all function which like like a reducer for your sagas.</blockquote><h4>Actions, The New Face of Blog</h4><p>Now, that we have some sagas that can watch our actions we need to update our now out of data actions.</p><p>If you have been reading along, you know that I follow the <a href="https://hackernoon.com/building-a-website-with-react-and-bulma-d655214bff2a">fractal file structure</a> which in the case of the <strong>store</strong> means the store/Blog.js is my public app facing code, and everything in the store/blog folder is private to the store. Sagas aren’t called by the app, the actions are. store/Blog.js currently contains our thunks but those are not necessary anymore, so we can delete them and replace them with the actions that the app will call that will trigger our sagas. We can then remove the actions.js file from our store/blog folder.</p><p>Here is the before and after of the Blog.js file</p><pre><strong>--- Before ---</strong></pre><pre>import * as contentful from &#39;contentful&#39;<br>import * as actions from &#39;./blog/actions&#39;</pre><pre>const client = contentful.createClient({<br>  space: &#39;qu10m4oq2u62&#39;,<br>  accessToken: &#39;f4a9f68de290d53552b107eb503f3a073bc4c632f5bdd50efacc61498a0c592a&#39;<br>})</pre><pre><br>export function loadBlog() {<br>  return dispatch =&gt; {<br>    dispatch(actions.blogLoading())<br>    return client.getEntries()<br>      .then(({items}) =&gt; {<br>        dispatch(actions.loadBlogSuccess(items))<br>      })<br>      .catch(error =&gt; {<br>        console.log(error)<br>        dispatch(actions.blogLoading(false))<br>      })<br>  }<br>}</pre><p>We get rid of all that logic because that the Saga’s job now, and replace it with a simple actions object that out app can call.</p><pre><strong>--- After ---</strong></pre><pre>import { createAction } from &#39;./Utilities&#39;<br>import * as types from &#39;./blog/types&#39;</pre><pre>export const actions = {<br>  pending: () =&gt; createAction(types.GET_BLOG_ASYNC.PENDING),<br>  success: (posts) =&gt; createAction(types.GET_BLOG_ASYNC.SUCCESS, { posts }),<br>  error: (error) =&gt; createAction(types.GET_BLOG_ASYNC.ERROR, { error })<br>}</pre><p>The <strong>createAction </strong>function doesn’t really do anything more then clean our code up by make it so that no matter how many things are actually passed in as the payload, this function always takes 2 arguments. I think in the future we can use this to help automate some of our async CRUD processes.</p><h4>One Last Thing, Initialize The Sagas</h4><p>In our store/index.js</p><pre>...<br>import createSagaMiddleware from &#39;redux-saga&#39;<br><strong>import { initSagas } from &#39;./rootSaga&#39;</strong></pre><pre>const sagaMiddleware = createSagaMiddleware()</pre><pre>export function configureStore() {<br> <strong> const store = </strong>createStore(<br>    rootReducer,<br>    composeWithDevTools(<br>      applyMiddleware(<br>        sagaMiddleware,<br>        thunk,<br>        reduxImmutableStateInvariant()<br>      )<br>    )<br>  )<br>  <strong>initSagas(sagaMiddleware)<br>  <br>  return store</strong><br>}</pre><p>Originally, we were return createStore, but initSagas need sagaMiddleware to be initialized first. So, we need to create the store then pass sagaMiddleware to initSages which will start all our watchers.</p><p>Instead doing something complex in the application root index to call initSagas we can just set a store variable to createStore, then call initSagas before return the store.</p><h4>That’s it!</h4><p>Granted, I only just barely scratched the surface of what Sagas can do, this is a good primer to getting them set up and actually working.</p><h3>Let’s Review</h3><ul><li>We Installed Sagas</li><li>We created some helpers for types and reducers.</li><li>Updated our types</li><li>Updated our reducer and learned some new technics</li><li>Created our first saga</li><li>Set up a rootSage</li><li>Moved our actions to the public class</li><li>Initialized our Saga on app loaded</li></ul><p>Yes this is way more complicated, and a fair of extra set up, and has kind of a step learning curve, and…. well you get the idea. But, it’s ultimately a better solution. It’s cleaner(ish), more scalable, more controllable, it’s real-time ready, and it’s infinitely easier to test which we will see in my <strong>next()</strong> article 😉</p><blockquote><strong>Next</strong> — Writing Unit Test for a React Blog with Redux Sagas</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4aa306854925" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/replacing-redux-thunks-with-redux-sagas-4aa306854925">Replacing Redux Thunks with Redux Sagas</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Photosynthesis]]></title>
            <link>https://medium.com/hacked-tabletop/photosynthesis-1ed77dc3388?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/1ed77dc3388</guid>
            <category><![CDATA[blue-orange-games]]></category>
            <category><![CDATA[review]]></category>
            <category><![CDATA[photosynthesis]]></category>
            <category><![CDATA[tabletop-gaming]]></category>
            <category><![CDATA[board-games]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Fri, 23 Feb 2018 16:01:02 GMT</pubDate>
            <atom:updated>2018-02-23T16:03:54.242Z</atom:updated>
            <content:encoded><![CDATA[<h4>Review</h4><h4>The Short Story: <a href="https://medium.com/hacked-tabletop/how-we-review-the-games-we-play-1f06c48d9867">Like It</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qzaJIUI564b-fU4fVh4RXQ.jpeg" /></figure><blockquote>Welcome to the world of photosynthesis, the green strategy board game! plant and shape the ever-changing forest as you cultivate your seeds and your strategy. Take your trees through their life-cycle, from seedling to full bloom to rebirth, and earn points as their leaves collect energy from the revolving sun’s rays. Carefully pick where you sow and when you grow, as trees in the shadows are blocked from light, and from points. This game features realistic gameplay and beautiful graphics.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0kVPC8bvX5A-33My0xdtCw.jpeg" /></figure><p><a href="https://boardgamegeek.com/boardgame/218603/photosynthesis"><strong>BoardGameGeek</strong></a><strong> Rating:</strong> 7.4<br><strong>MSRP:</strong> $32.93<br><strong>Players:</strong> 2–4<br><strong>Ages:</strong> 10+<br><strong>Playtime:</strong> 30–60 Min<br><strong>Publishers:</strong> <a href="http://www.blueorangegames.com/">Blue Orange Games</a><br><strong>Designers:</strong> Hjalmar Hach <br><strong>Year:</strong> 2017</p><h4>Concept</h4><p>Growing trees. Players take turns planting seeds and growing them in to trees by spending light points. Each player is awarded light points for each trees that bathe in the sunlight. As the sun rotates around the board, sunlight shines in a specific directs which causes the trees to cast shadows. Sometimes, trees will block the light to other trees and the taller the tree the bigger the shadow. Trees that are in the shadows don’t collect light points. When a tree grows big enough, it can be chopped down and players can collect Score Tokens. The player with highest score after 3 rounds is the winner.</p><h4>Quality</h4><p>Not bad considering it’s a bunch of <strong>3D cardboard punch-outs</strong> of trees! Our board wouldn’t lay flat and while trying to fix it the backing tore. The fold of the sun piece feels flimsy too, I don’t see it lasting forever.</p><h4>Artwork</h4><p>Fun painted cartoonish trees with cute little animals hiding in them. Each player is a different type/color of tree. It all just works. This one of the most beautiful games I’ve very played.</p><h4>Difficulty</h4><p>It’s not a very difficult game. Even the strategy is pretty straight forward. The rules were a little confusing at first considering it was only pages. They just were worded weird so it took us a few reads and playing the first 2 rounds to get it, but once we got it it was easy.</p><h4>Replayability</h4><p>— <strong>Moderately Low</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CsWj5gorvLjQ4s6seWxxEA.jpeg" /></figure><h3>Our Reviews</h3><blockquote>My rating: <strong>7<br></strong>by <a href="https://medium.com/u/1ef30430402c">Aaron Klaser</a></blockquote><blockquote><em>It’s a fun game that can be pulled off the shelf regularly, but it’s only fun to play once in an evening. It’s doesn’t have enough variety to keep it entertaining game after game. It’s kind of like checkers in way that it always starts the same and you always do that same thing. There is no randomness or chance, but there also isn’t enough strategy either. Don’t get me wrong, there is plenty of strategy and it really is a fun game, just not enough for me to play it more than once a game night.</em></blockquote><blockquote><em>The really winner is the visual appeal of this game. It’s absolutely beautiful, second only to Santorini. The 3D games trees, the color scheme, the little animals in the trees… it’s just fun and pleasant to look at.</em></blockquote><blockquote><em>One thing really bother me about this game is they called them light point instead of sun points, and they called the sun’s 6 turns around the board a Revolutions and not Seasons. It’s just a missed opportunity.</em></blockquote><blockquote>My rating: <strong>7<br></strong>by <a href="https://medium.com/u/189908a37abd">Beth Klaser</a></blockquote><blockquote><em>We have played Photosynthesis a handful of times since we picked it up at our local board game store {shout out to The Fantasy Shop}. The goal is to plant seeds, grow trees throughout there life-cycle and collect points.</em></blockquote><blockquote><em>I was a little disappointed that while there was strategy — there wasn’t a ton of strategy. It was based off the luck of planting your trees in just the right places and hoping your opponent didn’t notice until it is too late. If your opponent places there trees (same size or bigger) near yours when the sun comes around the board, it casts a shadow. No points for you — whomp whomp.</em></blockquote><blockquote><em>The winner for me is the visual aspects of the game itself. It has a unique theme. The trees are 3D. Each tree has something different going on be it — a rabbit, fox, or birds. The colors have that gorgeous fall touch to them all. The mechanics are simple enough that our eight year old could play, if she wanted too. Overall, I wouldn’t hesitate to grab photosynthesis off my game shelf. But, I would not play it more than once or twice in one sitting.</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sRxICev4B8RvmZmVrQzrYA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1DWRkK1Os0L9Yn2ijT6uEg.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rIA-YjZWUSAu3LAHMJgJmA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HMEm8GHRxr2yCuoZWZ2BZw.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LzjpBs6R5T_kIShKJZY__Q.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sapILCjkRn4H_AKpTWKCfA.jpeg" /></figure><p><em>But what does it all mean? Find out </em><a href="https://medium.com/@aaron.klaser/how-we-review-the-games-we-play-1f06c48d9867"><em>how we review the games we play</em></a><em>!</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BTNHBTUa-B06ScOa5knMEg.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cleP-ipHFD2bTBmz8GJPdA.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VwWcycxQP4yutxFQEZOrZw.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5Bew726uWGK3Msf_gvdNTQ.jpeg" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*If2H_wkP8TReXMCk-NjOEw.jpeg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1ed77dc3388" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hacked-tabletop/photosynthesis-1ed77dc3388">Photosynthesis</a> was originally published in <a href="https://medium.com/hacked-tabletop">Hacked Tabletop</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Adding Redux to a React Blog]]></title>
            <link>https://medium.com/hackernoon/adding-redux-to-a-react-blog-97f5fea606c2?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/97f5fea606c2</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[bulma]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[redux]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Thu, 15 Feb 2018 17:45:18 GMT</pubDate>
            <atom:updated>2018-02-28T15:18:55.802Z</atom:updated>
            <content:encoded><![CDATA[<h4>The React Blog Series: Part Four</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*VeM-5lsAtrrJ4jXH96h5kg.png" /></figure><p>This article is part four in a weekly series on building a blog with React and is an extension of the code created in previous parts.</p><h4>The React Blog Series</h4><p><a href="https://medium.com/front-end-hacking/building-a-website-with-react-and-bulma-d655214bff2a"><strong>Part One: </strong>Building a website with React and Bulma</a><br><a href="https://medium.com/@aaron.klaser/building-a-blog-with-react-and-contentful-fd538f68f6fb"><strong>Part Two:</strong> Building a Blog with React and Contentful</a><br><a href="https://medium.com/@aaron.klaser/import-your-medium-feed-into-react-ceadbaf785c7"><strong>Part Three:</strong> Import your Medium Feed into React</a><br><strong>Part Four:</strong> Adding a Redux to a React Blog<br><a href="https://hackernoon.com/replacing-redux-thunks-with-redux-sagas-4aa306854925"><strong>Part Five:</strong> Replacing Redux Thunks with Redux Sagas</a><br><strong>Part Six:</strong> (<em>In Progress</em>) Writing Unit Test for a React Blog with Redux Sagas</p><p>Here it is, the moment you have all be waiting for. — <strong><em>Redux!!!</em></strong></p><pre>npm install react-redux redux-thunk redux-immutable-state-invariant</pre><blockquote><strong>Note:</strong> You might also have install redux as a dev dependancy. Rumor is <strong>yarn</strong> solves this problem but you can also manually install it to the depentancies. npm install redux --save-dev</blockquote><h3>Adding a Store</h3><p>So this might be an unpopular opinion because I know everyone has a hard on for ReDucks, but thats not the right way to do it. When you put your store files in with your components, you’re combining your Presentation Layer with your application Business Layer and State. If something new comes along that replaces Redux, you won’t have to removed files from all our folders, just remove the whole store. But we will separate our file type in store.</p><h4>The Fractal Concept</h4><p>Remember we are using the fractal file structure key value concepts. In the store we are going to follow this pattern with<strong> key = public actions (thunks) </strong>and <strong>value = private (types reducers and actions), </strong>which means our app will never talk to the files in our folder. They will <strong>only talk to a thunk</strong> (the key, the public actions) which will talk to the files in its folder (the value, the private classes). In most cases the our <strong>thunk</strong> will get data from somewhere and at it to our store.</p><p>Confused yet? I though so.</p><p>Here is an example of a User store file structure. Our components will only ever talk to <strong>User.js</strong>, which in turn will talk the files in the user folder. This will make more sense as time goes on.</p><pre>src <br>- app <br>  - ... app code ...<br>- store<br>  - User.js<br>  - user<br>    - actions<br>    - reducer<br>    - types</pre><h4>Set Up</h4><p>In the src folder add a folder called <strong>store</strong>.</p><p>Then add three files: <strong>index.js</strong>, <strong>initialState.js</strong>, and <strong>rootReducer.js</strong></p><p>Setup index.js</p><pre>import { createStore, applyMiddleware } from &#39;redux&#39;<br>import { rootReducer } from &#39;./rootReducer&#39;<br>import reduxImmutableStateInvariant from &#39;redux-immutable-state-invariant&#39;<br>import thunk from &#39;redux-thunk&#39;</pre><pre>export function configureStore(initialState) {<br>  return createStore(<br>    rootReducer,<br>    initialState,<br>    applyMiddleware(<br>      thunk,<br>      reduxImmutableStateInvariant()<br>    )<br>  )<br>}</pre><p>Setup initialState.js</p><pre>export default {</pre><pre>}</pre><p>Setup rootReducer.js</p><pre>import { combineReducers } from &#39;redux&#39;</pre><pre>export const rootReducer = combineReducers({</pre><pre>})</pre><p>And to hook into the app open the apps index.js in the <strong>src folder </strong>and and the store and wrap the app.</p><pre>import React from &#39;react&#39;;<br>import ReactDOM from &#39;react-dom&#39;<br>import registerServiceWorker from &#39;./registerServiceWorker&#39;<br>import { BrowserRouter as Router } from &#39;react-router-dom&#39;<br>import App from &#39;./App&#39;</pre><pre><strong><em>// Redux Store<br></em>import { Provider } from &#39;react-redux&#39;<br>import { configureStore } from &#39;./src/store&#39;</strong></pre><pre>import &#39;./index.css&#39;</pre><pre><strong>const store = configureStore()</strong></pre><pre>ReactDOM.render((<br>  <strong>&lt;Provider store={store}&gt;</strong><br>    &lt;Router&gt;<br>      &lt;App /&gt;<br>    &lt;/Router&gt;<br>  <strong>&lt;/Provider&gt;</strong><br>), document.getElementById(&#39;root&#39;))<br>registerServiceWorker()</pre><p>ConfigureStore is our Store’s<strong> index.js</strong>, we are not currently passing any initial state during set up.</p><p>Also, if you need to app to retrieve any initial data, would call it just after we set the store to configureStore.</p><h3>Redux Blog Store</h3><p>Now that the easy part is over, let’s roll up our selves and make some magic.</p><p>In our store folder create a filed called <strong>Blog.js</strong> and folder called <strong>blog</strong>. In the blog folder create three fills: <strong>actions.js</strong>, <strong>reducer.js</strong>, and <strong>types.js</strong></p><p>Let’s start with our types. These are the constants our actions use call dispatches. First, we just need one get load our blog data into our store.</p><p>In store/blog/types.js add</p><pre><em>/**<br>* Blog Types<br>*/</em></pre><pre>export const LOAD_BLOG_SUCCESS = &#39;LOAD_BLOG_SUCCESS&#39;</pre><p>Before we go to far let’s add a blog object with an empty array for posts as our initial state for blog. This will be passed when setting up our reducer. It also allows the app to load without data yet with out throwing null exceptions.</p><p>In store/initialState.js add</p><pre>export default {<br>  blog: {<br>    posts: []<br>  }<br>}</pre><p>Next, let’s set up our reducer. This is what updates our state which is where our data gets stored. The actions will call these and via a switch statement it will look for the type that was called by the action.</p><p>In store/blog/reducer.js add</p><pre><em>/**<br>* Blog Reducer<br>*/</em><br>import initialState from &#39;../../store/initialState&#39;<br>import * as types from &#39;./types&#39;</pre><pre>export default function blogReducer(state = initialState.blog, action) {<br>  switch (action.type) {<br>    case types.LOAD_BLOG_POSTS_SUCCESS:<br>      return {<br>        ...state,<br>        posts: action.posts<br>      } <br>    default:<br>      return state<br>  }<br>}</pre><p>Next, we need to set up an action to dispatch the type and set its data as the payload (posts).</p><p>In store/blog/actions.js add</p><pre><em>/**<br>* Blog Actions<br>*/</em><br>import * as types from &#39;./types&#39;</pre><pre>export function loadBlogSuccess(post) {<br>  return { type: types.LOAD_BLOG_SUCCESS, post}<br>}</pre><p>Now, we need to include this in our rootReduce. The <strong>rootReducer combines all the store reducers</strong> into one giant mega reducer that creates a global state accessible every where in the application.</p><p>In store/rootReducer.js add</p><pre>import { combineReducers } from &#39;redux&#39;<br><strong>import blog from &#39;./blog/reducer&#39;</strong></pre><pre>export const rootReducer = combineReducers({<br><strong>  blog</strong><br>})</pre><blockquote><strong>Notice</strong> we didn’t import it as blogReducer, because what ever you call it in the combineReduces is how you access it in the rest of the app. <strong>Export default </strong>is what allows us to name things anything we want when we import the default function. We could have imported it as <strong>blogReducer</strong> a then the combineReducers functions done <strong>blog: blogReducer, </strong>but I personally think this looks better. #CodeIsArt</blockquote><h3>Testing that Redux is Working</h3><p>Right now our code is working. It doesn’t do anything yet because its not actually doing anything we can see, but it is working <strong><em>and</em></strong> I can prove it.</p><p>First you need the Redux Dev Tools extension for chrome. <a href="https://github.com/zalmoxisus/redux-devtools-extension">https://github.com/zalmoxisus/redux-devtools-extension</a></p><pre>npm install --save-dev redux-devtools-extension</pre><p>Then we need to hook it up. In store/index.js we are going to remove the <strong>initialState</strong> because we will be setting those in the reducers of each store, we will wrap our middleware with <strong>composeWithDevTools </strong>function.</p><pre>import { createStore, applyMiddleware } from &#39;redux&#39;<br>import { rootReducer } from &#39;./rootReducer&#39;<br>import reduxImmutableStateInvariant from &#39;redux-immutable-state-invariant&#39;<br>import thunk from &#39;redux-thunk&#39;<br><strong>import { composeWithDevTools } from &#39;redux-devtools-extension&#39;;</strong></pre><pre>export function configureStore() {<br>  return createStore(<br>    rootReducer,<br>    <strong>composeWithDevTools(</strong><br>      applyMiddleware(<br>        thunk,<br>        reduxImmutableStateInvariant()<br>      )<br>    <strong>)</strong><br>  )<br>}</pre><p>Then install the extension in chrome and open the chrome inspector (I usually right click on the page and click Inspector). In the top nav you should now have Redux option.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NFY2Ze0-4ljZjKwH_mbjBA.png" /></figure><p>Click it and refresh your app. The Redux tab sometimes has to be open when the app loads for it to work, this is because the connection is established on site load in the <strong>createStore(…) </strong>function.</p><p>Now, you should see your Redux state and history. It defaults to <strong>Diff</strong> so click the <strong>State</strong> tab and you should see your default object</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7YabeRhQBzYOx1laLGJ8tw.png" /></figure><p>Pretty cool, right!</p><h3>Putting Blog Data in the Blog Store</h3><p>Remember that in <strong>Blog.js </strong>file is the <strong>public actions </strong>(the thunk)<strong> </strong>of our store. This is where we will put our thunks and retrieve our data when the site loads.</p><p>We can reuse some of our logic from from app/Blog.js because it will no longer need to make the call to Contentful any more. That will happen in our thunk.</p><p>Let’s set up our thunk. Open store/Blog.js and add</p><pre>import * as contentful from &#39;contentful&#39;<br>import * as actions from &#39;./blog/actions&#39;</pre><pre>const client = contentful.createClient({<br>  space: &#39;qu10m4oq2u62&#39;,<br>  accessToken: &#39;f4a9f68de290d53552b107eb503f3a073bc4c632f5bdd50efacc61498a0c592a&#39;<br>})</pre><pre>const error = err =&gt; console.log(err)</pre><pre>export function loadBlog() {<br>  return dispatch =&gt;<br>    client.getEntries()<br>      .then(({items}) =&gt; {<br>        dispatch(actions.loadBlogSuccess(items))<br>      })<br>      .catch(error)<br>}</pre><blockquote>The thunk automatically passes dispatch into our functions which is what the first parameter there is doing.</blockquote><p>This code will make the call out to Contentful and the send that data to the loadBlogSuccess actions which will store the data on our blog state under posts.</p><p>But it wont happen automatically, we will need to call this function when the site loads.</p><p>We will do this by adding a line to our <strong>root</strong> index.js file</p><pre>...</pre><pre><em>// Redux Store<br></em>import { Provider } from &#39;react-redux&#39;<br>import { configureStore } from &#39;./store&#39;<br><strong>import { loadBlog } from &#39;./store/Blog&#39;</strong></pre><pre>import &#39;./index.css&#39;</pre><pre>const store = configureStore()<br><strong>store.dispatch(loadBlog())</strong></pre><pre>ReactDOM.render((</pre><pre>...</pre><p>We are telling the store that once its created, to dispatch the <strong>loadBlog() </strong>function.</p><p>When you refresh the app and check that Redux dev tools you should now see some data in <strong>blog &gt; posts</strong></p><h3>Using the Store in a Component</h3><p>This is a pretty easy step. Redux give us a function that we can use called mapStateToProps that, as you can probably guess, maps our redux state… to our page props. Then we “connect” the mapStateToProps and Blog.js to Redux.</p><p>Here‘s the magic, open app/Blog.js and remove our old code to get posts and add/update 8 lines of code.</p><pre>import React from &#39;react&#39;<br><strong>import { connect } from &#39;react-redux&#39;</strong><br>import * as contentful from &#39;contentful&#39;<br>import BlogItem from &#39;./blog/BlogItem&#39;<br>import PageHeader from &#39;./components/PageHeader&#39;<br>import PageContent from &#39;./components/PageContent&#39;</pre><pre>class Blog extends React.Component {<br>  render() {<br>    return (<br>      &lt;div&gt;<br>        &lt;PageHeader color=&quot;is-info&quot; title=&quot;Code Blog&quot;&gt;<br>          Your standard &lt;strong&gt;JavaScript&lt;/strong&gt; programming blog, albeit, probably not very good, but I will at least try to keep it entertaining. This blog is a chronological mix of random posts on Angular, React, Functional Programming, and my &lt;strong&gt;project walkthroughs&lt;/strong&gt;.<br>        &lt;/PageHeader&gt;<br>        &lt;PageContent&gt;<br>          { <strong>this.props.blog.posts.map</strong>(({fields}, i) =&gt;<br>            &lt;BlogItem key={i} {...fields} /&gt;<br>          )}<br>        &lt;/PageContent&gt;<br>      &lt;/div&gt;<br>    )<br>  }<br>}</pre><pre><strong>function mapStateToProps(state, ownProps) {<br>  return {<br>    blog: state.blog<br>  }<br>}</strong></pre><pre>export default <strong>connect(mapStateToProps)(Blog)</strong></pre><p>Blammo! Redux!</p><h3>Using Blog State Correctly</h3><p>I will be the first to admit that when I first started using Redux I was using it as a data store, and it’s so much more then that. It is your apps state, where its going, where its been, what its doing, what it knows… Using it only to store data is an insult to its existence. For example, our app runs fine right now because Contentful is awesome and fast and we only have very small about of data, but what if we had a lot of data or a slow connect. we don’t want to load a blank play for the user.</p><p>Let’s created a blog state for loading, that is we can display a loading icon while its getting data from Contentful.</p><p>store/initialState.js</p><pre>export default {<br>  blog: {<br>    <strong>loading: false,</strong><br>    posts: []<br>  }<br>}</pre><p>store/blog/types.js</p><pre><em>/**<br>* Blog Types<br>*/<br></em>export const BLOG_LOADING = &#39;BLOG_LOADING&#39;<br>export const LOAD_BLOG_SUCCESS = &#39;LOAD_BLOG_SUCCESS&#39;</pre><p>store/blog/reducer.js</p><pre><em>/**<br>* Blog Reducer<br>*/<br></em>import initialState from &#39;../../store/initialState&#39;<br>import * as types from &#39;./types&#39;</pre><pre>export default function blogReducer(state = initialState.blog, action) {<br>  switch (action.type) {<br>    case types.BLOG_LOADING:<br>      return {<br>        ...state,<br>        loading: action.isLoading<br>      }<br>    case types.LOAD_BLOG_SUCCESS:<br>      return {<br>        ...state,<br>        posts: action.posts,<br>        loading: false<br>      }<br>   default:<br>     return state<br>  }<br>}</pre><blockquote>Yes. State only contains posts and loading, yet we still spread it out. If you where keeping score, you might have noticed it earlier. It’s a good practice, because we are setting a new state object every time. Buy always spreading state first, we get all the previous values and don’t forget any, then the new fields simple overwrite the previous values. Like <strong>Object.assign({}, previouseState, { …newStateValues})</strong></blockquote><p>store/blog/actions.js</p><pre><em>/**<br>* Blog Actions<br>*/<br></em>import * as types from &#39;./types&#39;</pre><pre><strong>export function blogLoading(</strong>isLoading = true<strong>) {<br>  return { type: types.BLOG_LOADING, isLoading}<br>}</strong></pre><pre>export function loadBlogSuccess(posts) {<br>  return { type: types.LOAD_BLOG_SUCCESS, posts}<br>}</pre><blockquote>We are setting <strong>isloading</strong> to a default of true so we don’t we won’t normally have to pass any data in when dispatch blogLoading. <strong>BUT</strong>, by making it so we <strong>can</strong> pass in something like false, means we don’t have to create an action to set loading to false, like say if there was an error with loading.</blockquote><p>store/Blog.js</p><pre>import * as contentful from &#39;contentful&#39;<br>import * as actions from &#39;./blog/actions&#39;</pre><pre>const client = contentful.createClient({<br>  space: &#39;qu10m4oq2u62&#39;,<br>  accessToken: &#39;f4a9f68de290d53552b107eb503f3a073bc4c632f5bdd50efacc61498a0c592a&#39;<br>})</pre><pre>export function loadBlog() {<br>  return dispatch =&gt; <strong>{</strong><br>    <strong>dispatch(actions.blogLoading())<br></strong>    <strong>return </strong>client.getEntries()<br>      .then(({items}) =&gt; {<br>        dispatch(actions.loadBlogSuccess(items))<br>      })<br>      .catch(<strong>error =&gt; {<br>        console.log(error)<br>        dispatch(actions.blogLoading(false))<br>      }</strong>)<br>  <strong>}</strong><br>}</pre><blockquote>So we need to do some rearranging, we will clean this up later, but we need to able to call dispatch to call our actions for <strong>blogLoading</strong>.</blockquote><p>Now, you should see some additional actions being fired off in your Redux Dev tools.</p><h4>Update the View to Add a Loading Icon</h4><p>Back in app/Blog.js lets set up some placeholder text to make sure our logic works.</p><pre>&lt;div&gt;<br>  &lt;PageHeader color=&quot;is-info&quot; title=&quot;Code Blog&quot;&gt;<br>Your standard &lt;strong&gt;JavaScript&lt;/strong&gt; programming blog, albeit, probably not very good, but I will at least try to keep it entertaining. This blog is a chronological mix of random posts on Angular, React, Functional Programming, and my &lt;strong&gt;project walkthroughs&lt;/strong&gt;.<br>  &lt;/PageHeader&gt;<br>  <strong>{ this.props.blog.loading<br>    ? &lt;div&gt;Loading&lt;/div&gt;<br>    :</strong> &lt;PageContent&gt;<br>        { this.props.blog.posts.map(({fields}, i) =&gt;<br>          &lt;BlogItem key={i} {...fields} /&gt;<br>        )}<br>      &lt;/PageContent&gt;<br> <strong>}</strong><br>&lt;/div&gt;</pre><p>We are wrapping the previous code with ternary operator to handle the if statement. This means, if loading is true, we will show the loading div, else we show the content dev.</p><blockquote>If you’re like me, Contentful is just too fast and you can’t see it. We can wrap the dispatch on the getEntries success promise with a timeout. setTimeout(() =&gt; dispatch(actions.loadBlogSuccess(items)), 5000)</blockquote><blockquote>Now you should be able to see the loading div.</blockquote><blockquote><strong>Or</strong>, you can the history jump feature in the Redux Dev Tools. I’m gonna let you discover that little gem on your own ;)</blockquote><p>Now, in app/components create <strong>Loader.js </strong>which will be a styled component for our spinner.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/602c776f6b12353807670a0ee0ad0831/href">https://medium.com/media/602c776f6b12353807670a0ee0ad0831/href</a></iframe><p>Now back in our app/Blog.js let’s add the spinner. Replace &lt;div&gt;Loading&lt;/div&gt; with</p><pre>&lt;Loader className=&quot;has-text-primary&quot;&gt;&lt;/Loader&gt;</pre><p>and don’t forget to import the loader import { Loader } from ‘./components/Loader’</p><p>The Loader defaults to white, so you need to either hard code the color or using <strong>Bulma</strong> we can just give it the class <strong>has-text-$color </strong>(like has-text-primary)<strong>.</strong></p><h3>I found a bug :(</h3><p>Back in the clean up phase of <a href="https://medium.com/front-end-hacking/building-a-blog-with-react-and-contentful-fd538f68f6fb">Building a Blog with React and Contentful</a> I forgot to change the text for Read More and Back to Blog in the Blog Items Nav.</p><p>It’s an easy fix with a simple ternary operator in the &lt;Link&gt; using the to prop.</p><p>In app/blog/shared/BlogNav.js can the Link to this</p><pre>&lt;Link className=&quot;level-item button is-small is-link is-outlined&quot; to={to}&gt;<br>  <strong>{ to === &#39;/blog&#39; ? &#39;Back to Blog&#39; : &#39;Read More&#39;}</strong><br>&lt;/Link&gt;</pre><h3>Add the Store to the Medium Page</h3><p>Now we can do the exact same things for out Medium page. I’m not going to show you each and every step because that’s not how we learn, but I will list them out for you.</p><p><strong>Step 1:</strong> Create types<br><strong>Step 2:</strong> Add state to initial state<br><strong>Step 3:</strong> Set up reducers<br><strong>Step 4:</strong> Set up action<br><strong>Step 5:</strong> Add reducers to rootReducer<br><strong>Step 6</strong>: Setup our thunk <em>(hint below)</em></p><pre>import * as actions from &#39;./medium/actions&#39;<br>import axios from &#39;axios&#39;</pre><pre>const fetchPosts = () =&gt; axios.get(`https://cors.now.sh/https://us-central1-aaronklaser-1.cloudfunctions.net/medium?username=@aaron.klaser`)</pre><pre>const setPosts = ({data}) =&gt; Object.values(data.payload.references.Post).map(<br>  ({ id, title, createdAt, virtuals, uniqueSlug }) =&gt; Object.assign({},{<br>    title,<br>    createdAt,<br>    subtitle: virtuals.subtitle,<br>    image: virtuals.previewImage.imageId ? `https://cdn-images-1.medium.com/max/800/${virtuals.previewImage.imageId}` : null,<br>    url: `https://medium.com/@aaron.klaser/${uniqueSlug}`<br>  })<br>)</pre><pre>export const loadMedium = () =&gt; async dispatch =&gt; {<br>  dispatch(actions.mediumLoading())<br>  const data = await fetchPosts()<br>  return dispatch(actions.loadMediumSuccess(setPosts(data)))<br>}</pre><p><strong>Step 7:</strong> Add state to component</p><p>Test it and move on with life. You are now a Redux master!</p><h3>Let’s Review</h3><ul><li>Add Redux and friends to your project.</li><li>Set up the Store using fractal file sturcture</li><li>Set up the store’s types reducer and actions for loading data</li><li>Set up the store’s thunk, which is our public actions class</li><li>Add the store to the page component</li><li>Added a loading state and loading animation</li><li>Fixed a previews bug</li><li>Added the store to the Medium page</li></ul><blockquote>Next —<a href="https://hackernoon.com/replacing-redux-thunks-with-redux-sagas-4aa306854925"> Replacing Redux Thunks with Redux Sagas</a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=97f5fea606c2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/adding-redux-to-a-react-blog-97f5fea606c2">Adding Redux to a React Blog</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Import your Medium Feed into React]]></title>
            <link>https://medium.com/hackernoon/import-your-medium-feed-into-react-ceadbaf785c7?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/ceadbaf785c7</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[redux]]></category>
            <category><![CDATA[medium]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Mon, 12 Feb 2018 16:30:31 GMT</pubDate>
            <atom:updated>2018-02-28T15:19:19.278Z</atom:updated>
            <content:encoded><![CDATA[<h4>The React Blog Series: Part Three</h4><p>This article is part three in a weekly series on building a blog with React and is an extension of the code created in previous parts.</p><h4>The React Blog Series</h4><p><a href="https://medium.com/front-end-hacking/building-a-website-with-react-and-bulma-d655214bff2a"><strong>Part One: </strong>Building a website with React and Bulma</a><br><a href="https://medium.com/@aaron.klaser/building-a-blog-with-react-and-contentful-fd538f68f6fb"><strong>Part Two:</strong> Building a Blog with React and Contentful</a><br><strong>Part Three:</strong> Import your Medium Feed into React<br><a href="https://medium.com/@aaron.klaser/adding-redux-to-a-react-blog-97f5fea606c2"><strong>Part Four:</strong> Adding a Redux to a React Blog</a><br><a href="https://hackernoon.com/replacing-redux-thunks-with-redux-sagas-4aa306854925"><strong>Part Five:</strong> Replacing Redux Thunks with Redux Sagas</a><br><strong>Part Six:</strong> (<em>In Progress</em>) Writing Unit Test for a React Blog with Redux Sagas</p><p>If your not blogging on medium yet, you should. It’s so easy.</p><p>This week we are going to add a feed to our website so we have a kind of alternative blog.</p><p>First let’s look at what a medium feed looks like. We can and medium content back in JSON format by adding <strong>?format=json</strong> to the end of the url. Here is my feed.</p><p><a href="https://medium.com/@aaron.klaser/latest?format=json">https://medium.com/@aaron.klaser/<strong>latest?format=json</strong></a></p><p>This will return a massive chunk of JSON, but we only care about payload.references.Post</p><blockquote>I think it’s fair to note that this is not actually a “feed”, this is basically the content that Medium uses to display <strong>previews</strong> of my latest post on my Medium profile, not the complete posts themselves. Medium provides an RSS feed for which does contain more information like a traditional blog feed, however it can not be returned any JSON, only XML. I like the idea of this just being previews with links back to my Medium articles, on Medium, and it’s a bonus to not have to parse XML. But, if you would like to use the actual Medium feed these concepts are almost identical and you would use the url <a href="https://medium.com/@aaron.klaser">https://medium.com/</a><a href="https://medium.com/@aaron.klaser"><strong>feed/</strong></a><a href="https://medium.com/@aaron.klaser">@aaron.klaser</a></blockquote><p>But we need a page to put it on. I <a href="https://medium.com/front-end-hacking/building-a-website-with-react-and-bulma-d655214bff2a">already have a tab for it in the Nav</a> we just need to create a page to put it on. The basic set up follows a lot of the steps in <a href="https://medium.com/front-end-hacking/building-a-blog-with-react-and-contentful-fd538f68f6fb">my last article</a>, so I’m going move quickly though the basic page setup.</p><p>In the <strong>app </strong>folder, create a file called Medium.js</p><pre>import React from &#39;react&#39;<br>import PageHeader from &#39;./components/PageHeader&#39;<br>import PageContent from &#39;./components/PageContent&#39;<br>import axios from &#39;axios&#39;</pre><pre>class Medium extends React.Component {</pre><pre>  state = {<br>    posts: []<br>  }</pre><pre>  componentDidMount() {<br>    this.fetchPosts().then(this.setPosts)<br>  }</pre><pre>  fetchPosts = () =&gt; axios.get(`https://cors.now.sh/https://us-central1-aaronklaser-1.cloudfunctions.net/medium?username=@aaron.klaser`)</pre><pre>  setPosts = response =&gt; {<br>    this.setState({<br>      posts: response<br>    })<br>  }</pre><pre>  render() {<br>    return (<br>      &lt;div&gt;<br>        &lt;PageHeader color=&quot;is-dark&quot; title=&quot;Medium&quot;&gt;<br>          Medium is where I ramble and rant and tell stories. I orginally was going to use it as a coding blog, I don&#39;t like having to use Gist for all my code snippets. So I created this site.<br>          &lt;br /&gt;&lt;br /&gt;<br>          &lt;a className=&quot;button is-inverted is-outlined&quot; href=&quot;https://medium.com/@aaron.klaser&quot; target=&quot;_blank&quot;&gt;<br>            View My Medium<br>            &lt;span className=&quot;icon&quot; style={{ marginLeft: 5 }}&gt;<br>              &lt;i className=&quot;fab fa-lg fa-medium&quot;&gt;&lt;/i&gt;<br>            &lt;/span&gt;<br>          &lt;/a&gt;<br>        &lt;/PageHeader&gt;<br>        &lt;PageContent&gt;<br>          &lt;pre&gt;{JSON.stringify(this.state.posts, null, 2)}&lt;/pre&gt;<br>        &lt;/PageContent&gt;<br>      &lt;/div&gt;<br>    )<br>  }<br>}</pre><pre>export default Medium</pre><p>Then set up the routing for this page and update the item for Medium to point to this page.</p><h4>This was not easy to figure out</h4><p>So as we are building this locally Medium blocks localhost for cors. You can tell fetch to run no cors mode but then you get an opaque response with no data. I found that I could get around this by running everything through an express server but I don’t want my site to need a back end. I also tryied running it through something like cors-anywhere and cors.now. They where just blocked, and for good reasons honestly.</p><h3>Serverless Architecture to the Rescue</h3><p>I was able to fix this using Firebase Cloud Functions to make the call for me. However, firebase still throw cors errors but they where not blocked by <strong>cors.now</strong>.</p><p>My function looks like this, it’s ugly but I got it working and I’m moving on.</p><blockquote>Medium returns a weird prefix ])}while(1);&lt;/x&gt; to prevent JSON Hacking (:P) so we’ll just remove that and return our data.</blockquote><pre>const functions = require(&#39;firebase-functions&#39;);<br>var request = require(&#39;request&#39;);</pre><pre>exports.medium = functions.https.onRequest((req, res) =&gt; {<br>  if(!req.query.username) {<br>    return res.status(400).send(&#39;Error: You need to include query param ?username=@yourUsername&#39;);<br>  }</pre><pre>  const url = `https://medium.com/${req.query.username}/latest?format=json`;</pre><pre>  return request(url,(error, response, body) =&gt; {<br>    const prefix = `])}while(1);&lt;/x&gt;`<br>    const strip = payload =&gt; payload.replace(prefix, ``)<br>    res.send(JSON.parse(strip(body)));<br>  });<br>})</pre><p>I have set up and endpoint in <strong>firebase functions</strong> that <strong>you can use </strong>or you can try to create your own. I might even create an article on it because it turns out its pretty cool.</p><p>https://cors.now.sh/https://us-central1-aaronklaser-1.cloudfunctions.net/medium?username=<strong>@yourUsername</strong></p><p>Just update your username and you should see your json in the pre block.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3ys_HSGPBB5pJWsCX2fnUw.png" /></figure><h3>Extracting the Data For Our Posts</h3><p>There is an <strong>easy way</strong> but its dirty, then there is a <strong>hard(ish) way</strong> but it’s clean.</p><h4>The Easy Way:</h4><p>Use Object.values() to convert the Posts node to an array of objects. This gets use our data but its ALL our date and we wont be able to spread that out over our MediumItem (we will build shortly)</p><pre>setPosts = (<strong>{data}</strong>) =&gt; {<br>  this.setState({<br>    posts: <strong>Object.values(data.payload.references.Post)</strong><br>  })<br>}</pre><p>But when you run the app you will see the pre blocks have the extracted Post data, but that also have a mountain of data you don’t need. The hard way solves this.</p><h4><strong>The Hard Way:</strong></h4><p><em>(Recommended) </em>Let’s extract the only data we need in our post. That would be: <strong>createdAt, image, title, subtitle, description,</strong> and the <strong>url </strong>back to the post on Medium.</p><pre>setPosts = ({<strong>data</strong>}) =&gt; {</pre><pre>  <strong>const { Post } = data.payload.references</strong></pre><pre><strong>  const posts = Object.values(Post).map(({ id, title, createdAt, virtuals, uniqueSlug }) =&gt; Object.assign({},{<br>      title,<br>      createdAt,<br>      subtitle: virtuals.subtitle,<br>      image: virtuals.previewImage.imageId ? `https://cdn-images-1.medium.com/max/800/${virtuals.previewImage.imageId}` : null,<br>      url: `https://medium.com/@aaron.klaser/${uniqueSlug}`<br>    })<br>  )</strong></pre><pre>  this.setState({<br>    posts<br>  })<br>}</pre><p>Now when we run our app, it should only print out the five fields we need from the giant blob of JSON, all ready to be spread out in our MediumItem object which we will build…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nflbvxdkEovhfjtOwwNlFw.png" /></figure><p>Now!</p><h3>Building the MediumItem Component</h3><p>In our app folder, create a medium folder and inside that create MediumItem.js</p><p>This will look similar to our blog Item, but since medium doesn’t give us the content, we won’t need a MediumPost like we did with the Blog, so we wont have any shared items and we will just put it all in this one component.</p><pre>import React from &#39;react&#39;<br>import moment from &#39;moment&#39;</pre><pre>const MediumItem = ({title, createdAt, subtitle, image, url}) =&gt; (<br>  &lt;div className=&quot;box is-paddingless card&quot;&gt;<br>    { image<br>    ? (&lt;div className=&quot;card-image&quot;&gt;<br>         &lt;figure className=&quot;image&quot;&gt;<br>           &lt;img src={image} /&gt;<br>         &lt;/figure&gt;<br>       &lt;/div&gt;)<br>     : &quot;&quot; }<br>    &lt;div className=&quot;card-content&quot;&gt;<br>      &lt;div className=&quot;media&quot;&gt;<br>        &lt;div className=&quot;media-content&quot; style={{ overflow: &#39;inherit&#39; }}&gt;<br>        &lt;p className=&quot;title is-4&quot;&gt;{title}&lt;/p&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>    &lt;div className=&quot;content&quot;&gt;<br>      { subtitle }<br>    &lt;/div&gt;<br>    &lt;nav className=&quot;level&quot;&gt;<br>      &lt;div className=&quot;level-left&quot;&gt;<br>        &lt;div className=&quot;tags has-addons&quot;&gt;<br>          &lt;span className=&quot;tag is-primary&quot;&gt;{moment(createdAt).format(&#39;MMM Do&#39;)}&lt;/span&gt;<br>          &lt;span className=&quot;tag&quot;&gt;{moment(createdAt).format(&#39;YYYY&#39;)}&lt;/span&gt;<br>        &lt;/div&gt;<br>      &lt;/div&gt;<br>      &lt;div className=&quot;level-right&quot;&gt;<br>        &lt;a className=&quot;button is-small is-link is-outlined&quot; target=&quot;_blank&quot; href={url}&gt;<br>          Read on Medium<br>        &lt;/a&gt;<br>      &lt;/div&gt;<br>    &lt;/nav&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;<br>)</pre><pre>export default MediumItem</pre><p>It should look something like this</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3Q8swqqgBa-7PFo1Tbk97w.png" /></figure><p>And there you have it kids! You now have a Medium feed in your Blog that links back to your Medium posts. Start clapping!!</p><h3>Let’s Review</h3><ul><li>We set up a page for our Medium Feed</li><li>We linked to Serverless Function to retrieve our Medium feed</li><li>We extracted the data we need from our Medium content</li><li>We set up a simple MediumItem component.</li></ul><blockquote>Next — <a href="https://medium.com/@aaron.klaser/adding-redux-to-a-react-blog-97f5fea606c2">Adding a Redux to a React Blog</a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ceadbaf785c7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/import-your-medium-feed-into-react-ceadbaf785c7">Import your Medium Feed into React</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Who is Hacked Tabletop]]></title>
            <link>https://medium.com/hacked-tabletop/who-is-hacked-tabletop-and-what-is-our-future-5523ba27bacb?source=rss-1ef30430402c------2</link>
            <guid isPermaLink="false">https://medium.com/p/5523ba27bacb</guid>
            <category><![CDATA[hacked-tabletop]]></category>
            <category><![CDATA[tabletop]]></category>
            <category><![CDATA[board-games]]></category>
            <dc:creator><![CDATA[Aaron Klaser]]></dc:creator>
            <pubDate>Mon, 05 Feb 2018 17:27:24 GMT</pubDate>
            <atom:updated>2018-02-05T17:41:19.759Z</atom:updated>
            <content:encoded><![CDATA[<h4>And What is Our Future?</h4><p>At the time of writing this, Hacked Tabletop is a husband-wife combo featuring me (<a href="https://medium.com/u/1ef30430402c">Aaron Klaser</a>) and <a href="https://medium.com/u/189908a37abd">Beth Klaser</a>. We are not writers or bloggers by any means, but instead we are passionate board game enthusiasts who just want to give back to other board game and tabletop enthusiasts. Over the last 2 years of diving head first into our addiction of buying almost any game that sounded interesting, we have noticed a few gaps in the community.</p><p>We know there are 100’s, maybe 1,000’s of people out there reviewing games. We have to bring it to be able to make a name for ourselves, but we may have a few tricks up our sleeves to help set us apart from all the others.</p><h4>The Problem</h4><p>My biggest complaint about board game reviews has always been the quality of reviews, visually. Most reviews are well written but wither it’s the video or the photos, they are all just… crap. I’m sorry. I know how hard everyone works to put these together,I mean no insult, but it’s time to step it up.</p><p>Another common issue is the lack of quality How-To videos. Games these days are WAY more difficult then they were when I was kid. The exceptions are <a href="https://www.youtube.com/channel/UCGK9n7svoIjuaQfRIBJXkqQ">Watch It Played</a> and <a href="https://www.youtube.com/playlist?list=PL7atuZxmT955LMr27MkQzrV2adQtU3o5U">Wil Wheaton’s Tabletop</a> — tutorials are either not well explained or the quality is to poor for it to be beneficial.</p><h4>Our Solutions</h4><p>We are planning to seriously set up quality. Much to my wife’s dismay, we have invested butt-loads of money into gear; 4k Cameras, professional lighting, editing software, streaming rigs, and web cams. You will no longer see poorly lit photographs from terrible angles or some cheap web cam sat in the middle of the of room that’s really hard to see and hear.</p><p>There is going to be some trial and error to get it right, but we currently have over 160+ games in our constantly growing collection of board games to practice with.</p><h4>We plan to bring you</h4><p><strong><em>Reviews &amp; Previews</em></strong><br>Unbiased reviews and previews including high quality professional photographs. We don’t think it’s the greatest game ever made just because it has mini’s. Although, I (<a href="https://medium.com/u/189908a37abd">Beth Klaser</a>) do love some mini’s. We have designed a processes to determine our rating — which includes playing a game at least <strong>five </strong>times no matter how terrible the game may be. <a href="https://medium.com/hacked-tabletop/how-we-review-the-games-we-play-1f06c48d9867">Learn More</a></p><p><strong><em>How To’s &amp; Unboxings</em></strong><br>This is not new to the community. However, we will be using professional equipment such as lighting and a 4k camera to bring the highest quality to our tutorials, How To’s, and unboxings.</p><p><strong><em>Learn it Together</em></strong><br><em>Learn it Together</em> well be a live streamed web series where we sit down and try to learn a new game together live on Twitch (and maybe a few other places).</p><h3>HackedTabletop.com</h3><p>Don’t worry, we have a site coming! Medium is our temporary home. We ended up choosing Medium because it is easy to set up and because reviews can be written fairly quickly. I (<a href="https://medium.com/u/1ef30430402c">Aaron</a>) am a web developer by day, and have been for the last ten years. I am building our site using React and Contentful. There may even be an easy to use <strong>app</strong> in the works.</p><h3>Want Your Gamed Featured?</h3><p>Featured games will get a little extra attention. They will get a photo of there game displayed front and center on our page.</p><p>We plan to have a game featured starting on March 1st, 2018. If you are interested in having your game featured on our site, drop a line to <a href="http://Hackedtabletop@gmail.com/"><strong><em>Hackedtabletop@gmail.com</em></strong></a><strong><em>.</em></strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5523ba27bacb" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hacked-tabletop/who-is-hacked-tabletop-and-what-is-our-future-5523ba27bacb">Who is Hacked Tabletop</a> was originally published in <a href="https://medium.com/hacked-tabletop">Hacked Tabletop</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>