<?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 David Kerr on Medium]]></title>
        <description><![CDATA[Stories by David Kerr on Medium]]></description>
        <link>https://medium.com/@dkerrious?source=rss-1fc02e3021da------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*GzdZZe11aDgN2lxHwesLXQ.jpeg</url>
            <title>Stories by David Kerr on Medium</title>
            <link>https://medium.com/@dkerrious?source=rss-1fc02e3021da------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 12 Nov 2026 08:19:13 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@dkerrious/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[Tips for Reviewing Pull Requests]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@dkerrious/tips-for-reviewing-pull-requests-e990545cc059?source=rss-1fc02e3021da------2"><img src="https://cdn-images-1.medium.com/max/848/1*vlnjcAP9wzj1RNCdSCL9_g.jpeg" width="848"></a></p><p class="medium-feed-snippet">Reviewing pull requests is a vital part of the software development process.</p><p class="medium-feed-link"><a href="https://medium.com/@dkerrious/tips-for-reviewing-pull-requests-e990545cc059?source=rss-1fc02e3021da------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@dkerrious/tips-for-reviewing-pull-requests-e990545cc059?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/e990545cc059</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[git]]></category>
            <category><![CDATA[pull-request]]></category>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Fri, 16 Sep 2022 01:15:36 GMT</pubDate>
            <atom:updated>2023-11-17T16:37:24.216Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Advice for anyone starting as a Junior Developer]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@dkerrious/advice-for-anyone-starting-as-a-junior-developer-f1f6e6c78c67?source=rss-1fc02e3021da------2"><img src="https://cdn-images-1.medium.com/max/880/1*hKWJpVQOp_y229KZR5XNLA.jpeg" width="880"></a></p><p class="medium-feed-snippet">This post focuses on what a junior developer should do when starting a job to ensure their success in a position. My sister is finishing a&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/@dkerrious/advice-for-anyone-starting-as-a-junior-developer-f1f6e6c78c67?source=rss-1fc02e3021da------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@dkerrious/advice-for-anyone-starting-as-a-junior-developer-f1f6e6c78c67?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/f1f6e6c78c67</guid>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Thu, 10 Sep 2020 01:15:05 GMT</pubDate>
            <atom:updated>2020-09-11T14:49:30.556Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Finding Career Clarity]]></title>
            <link>https://medium.com/@dkerrious/finding-career-clarity-8a057808d1bc?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/8a057808d1bc</guid>
            <category><![CDATA[career-advice]]></category>
            <category><![CDATA[career-change]]></category>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Fri, 04 Sep 2020 16:43:07 GMT</pubDate>
            <atom:updated>2020-09-04T16:56:15.766Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bPVZbn7lKJzeo_bMm1wuSQ.png" /></figure><p>This post is several weeks overdue. Weeks ago, a friend of Lindsay and myself was generous enough to invite me to her podcast to talk about my experience changing careers. The whole being recorded thing was a new experience for me, and one I am curious about moving forward.</p><p>Here is a <a href="https://getcareerclarity.com/episode55/">link to the podcast</a> on the career clarity website. It is also available wherever you listen to your podcasts searching for Career Clarity. I encourage anyone who stumbles on this to listen and read my additional comments below for some brutal honesty. Thanks again for the opportunity Lisa, I look forward to following Career Clarity and cannot recommend her or her company enough.</p><p>It is funny the things you say when you are put on the spot sometimes. I don’t know that the microphone contributes much, but I am sure it plays a part. As I listened back, there were a couple of points where I was uncomfortable with how I presented my situation:</p><ul><li>I presented my foray into the blog platform business as something I financed myself. My dad gave me cash to explore this idea (I want to say 10k). He always played these games with money, some fruitful, some not. During this time, the game was keeping small sums of cash out of banks to avoid higher tuitions on the FAFSA. This money went primarily to legal fees (people posting content on YOUR site can land you in jail quickly without some terms of use documents), domain expenses, and some other expenses (some stupid as I look in hindsight). My dad has always fixated on helping my sister and I pay off our student loans, so I think he saw the money as an investment in either me or the business. Hopefully, I can make this one of his best investments even though the corporation is no more.</li><li>I also presented this as something I came to myself. The idea itself is not novel, providing a content platform that returns ad revenue to its members. What made me take “the leap” was the people I had around me. I had a friend (Alex Eisenach) who was confident in his ability to deliver the technology in a short time frame if I could remove business hurdles. We both learned an immense amount. He put in tons of hours to build something fun; maybe we will get it back out there one of these years.</li><li>I presented my year off as being one which I was primarily able to finance. While this is true, I had the support of my family in a few key ways. First, my grandmother was near the end of her life and spreading money to her grandchildren and children legally in increments each year. I probably benefited to the tune of 10–15k during the period of attending school and working for next to nothing for a startup, making this a more comfortable experience.</li></ul><p><a href="https://getcareerclarity.com/the-career-clarity-approach/">Link to Career Clarity</a></p><p><a href="https://www.linkedin.com/in/lisamlewis/">Link to Lisa Lewis on LinkedIn</a></p><p><em>Originally published at </em><a href="https://prying.io/culture/2020/09/05/finding-career-clarity.html"><em>https://prying.io</em></a><em> on September 5, 2020.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8a057808d1bc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[You Can Book Me]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@dkerrious/you-can-book-me-71e0749c1baa?source=rss-1fc02e3021da------2"><img src="https://cdn-images-1.medium.com/max/2060/0*-68nyUHKjgC7hbEl.png" width="2060"></a></p><p class="medium-feed-snippet">Over the past three months, I have realized everything that, five years ago, I would have said is &#x201C;enough.&#x201D; I moved into a dream home. My&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/@dkerrious/you-can-book-me-71e0749c1baa?source=rss-1fc02e3021da------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@dkerrious/you-can-book-me-71e0749c1baa?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/71e0749c1baa</guid>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Mon, 06 Jul 2020 01:15:05 GMT</pubDate>
            <atom:updated>2020-07-07T13:10:26.592Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Caching Options in Elixir]]></title>
            <link>https://medium.com/@dkerrious/caching-options-in-elixir-f415149eda6b?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/f415149eda6b</guid>
            <category><![CDATA[benchmarking]]></category>
            <category><![CDATA[erlang]]></category>
            <category><![CDATA[caching]]></category>
            <category><![CDATA[phoenix]]></category>
            <category><![CDATA[elixir]]></category>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Sun, 01 Sep 2019 01:15:05 GMT</pubDate>
            <atom:updated>2019-09-04T00:17:10.263Z</atom:updated>
            <content:encoded><![CDATA[<h3>Caching Options in an Elixir Application</h3><p>Are you overworking your database? Are you paying for each query? Are you returning the same payloads again and again? It might be time to think about caching and Elixir and Erlang make this easy with several in-memory options. I learned about the 2 Erlang options at ElixirConf 2019 and felt compelled to write this post, so a big thank you to all those who shared!</p><p>There are four different standard methods for temporarily storing and accessing data in a simple Elixir application. This article compares those methods, provides usage examples, and offers my personal opinion on when to use each tool. Here are those 4 simple strategies of storing data for quicker retrieval than going to a database:</p><p>If you wonder why this matters, here is a benchmarking test comparing the time it creates a table, set 100 key-value pairs, and remove 100 key-value pairs. The results show that :ets is significantly faster than Agent (and therefore GenServer).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*LKNDgzusUwffpGJf.png" /></figure><h3>Using Agent</h3><p><strong>Example:</strong></p><pre>Agent.start_link(fn -&gt; %{} end, name: :us_state_abbreviations)</pre><pre>-&gt; {:ok, #PID&lt;0.102.0&gt;}</pre><pre># or to clarify the first argument and show a namespace error</pre><pre>initial_state_callback = fn -&gt; %{}</pre><pre>Agent.start_link(intial_state_callback, name: :us_state_abbreviations)</pre><pre>-&gt; {:error, {:already_started, #PID&lt;0.102.0&gt;}}</pre><pre># Accessing all Agent data</pre><pre>Agent.get(:us_state_abbreviations, &amp;(&amp;1))</pre><pre>-&gt; %{}</pre><pre># Checking for a certain value, in the lookup</pre><pre>key = &quot;OH&quot;</pre><pre>Agent.get(:us_state_abbreviations, fn(states) -&gt; Map.get(states, key, &quot;N/A&quot;) end)</pre><pre>-&gt; &quot;N/A&quot;</pre><pre># Updating Agent data</pre><pre>updated_state = %{&quot;OH&quot; =&gt; &quot;Ohio&quot;}</pre><pre>Agent.update(:us_state_abbreviations, fn(_) -&gt; updated_state end)</pre><pre>-&gt; :ok</pre><pre># Confirming the updates</pre><pre>key = &quot;OH&quot;</pre><pre>Agent.get(:us_state_abbreviations, fn(states) -&gt; Map.get(states, key, &quot;N/A&quot;) end)</pre><pre>-&gt; &quot;Ohio&quot;</pre><p><strong>Pros:</strong> Agent can be accessed from anywhere in the application, provided the requester has the name used to register Agent. If a single process crashes, Agent state persists. Agent is very lightweight to implement and use.</p><p><strong>Cons:</strong> If your entire application crashes, Agent state is lost.</p><p><strong>Opinion:</strong> In most cases, Agent is reimplementing given behaviors of GenServer state management. If you find yourself sharing cached data between modules of your application, that is a sign of an architecture design problem. Agent under the hood is a simple GenServer you don&#39;t manage, so in most situations, it is better to build your own.</p><h3>Using GenServer</h3><p><strong>Example:</strong> I am not going to rewrite the hex documentation, so please look here(https://hexdocs.pm/elixir/GenServer.html) for information about GenServers. The quick summary is that GenServers maintain state through their return values, and each function called through a handle_cast, handle_call, or handle_info includes the current state as an input. A queue of internal messages is maintained, and the state updates sequentially as the servers process this call stack.</p><p><strong>Pros:</strong> GenServers easily integrate into your supervision tree, with built-in hooks to manage and recover state. If you need to consume data from a GenServer elsewhere in your application, you implement accessor methods on the GenServer.</p><p><strong>Cons:</strong> Requires more direct and considered state management, meaning more work to set-up, configure, and test your module.</p><p><strong>Opinion:</strong> Leaning on GenServers for caching, recovering, and managing state is the best method for caching in Elixir. While it does feel like more work is required, this work gives the additional control developers need to lock down their data flows.</p><h3>Using ETS</h3><p><strong>Example:</strong></p><pre># create a lookup table</pre><pre>:ets.new(:us_state_abbreviations, [:set, :public, :named_table])</pre><pre>-&gt; table_ref</pre><pre># lookup a value when it doesn&#39;t exist</pre><pre>:ets.lookup(state.table_ref, key)</pre><pre>-&gt; []</pre><pre># lookup when it does</pre><pre>-&gt; [{key, value}, ...]</pre><pre># how to handle these responses</pre><pre>def lookup(module \\ __MODULE__, key) do</pre><pre>GenServer.call(module, {:lookup, key})</pre><pre>end</pre><pre>def handle_call({:lookup, key}, _from, state) do</pre><pre># good practice to save the ets table ref in state</pre><pre>case :ets.lookup(state.table_ref, key) do</pre><pre># match against key for corelated value</pre><pre>[{^key, value}] -&gt; {:reply, {:ok, value}, data}</pre><pre>[] -&gt; {:reply, {:error, :not_found}, data}</pre><pre>end</pre><pre>end</pre><pre># insert a record</pre><pre>:ets.insert(state.table_ref, {key, value})</pre><pre>-&gt; true</pre><pre># nuke table</pre><pre>:ets.delete_all_objects(state.table_ref)</pre><pre>-&gt; true</pre><p><strong>Pros:</strong> Persists past process crash, but not past an application crash. Fast reads, fast writes. It is a simple key-value store of tuples ({key, value}).</p><p><strong>Cons:</strong> Requires more parsing of responses. Less flexible lookup. There are extensions for :ets which allow for searching by Ecto structs which I will post about in the future.</p><p><strong>Opinion:</strong> For most applications, using GenServers is sufficient for performance. Often, GenServer state contains business logic, and the :ets state contains distilled information for external consumption. I think of this as the external cache whereas the GenServer state is primarily an internal cache. A typical pattern is attaching these returned table references to a GenServer state.</p><h3>Using Persistent Term</h3><p><strong>Example:</strong></p><pre>:persistent_term.put(&quot;OH&quot;, &quot;Ohio&quot;)</pre><pre>-&gt; :ok</pre><pre>:persistent_term.get(&quot;OH&quot;)</pre><pre>-&gt; &quot;Ohio&quot;</pre><p><strong>Pros:</strong> :persistent_term has much faster read times, even than :ets. Data persists past an application crash, meaning it is an ideal place for certain nuggets of information. Shared across all nodes in a cluster.</p><p><strong>Cons:</strong> Slow write times since updates sync across all nodes.</p><p><strong>Opinion:</strong> I haven’t found the right use for this yet in any of my applications, but it seems useful for isolated problems at scale.</p><h3>Summary</h3><p>In summary, you should probably use GenServers instead of Agent in most situations. Agent is a lazier GenServer with more default behaviors. For performance and flexibility, use both :ets and a GenServer. In GenServer state, store a reference to :ets tables and use GenServer calls to lookup against this table because :ets is more performant. If you are operating a cluster, and aren&#39;t updating these read values as frequently, consider using :persistent_term. It updates across all connected nodes in a cluster (using a HashRing library like ExHashRing), so while this is powerful, it is also expensive to write data through this system. Read of this data is faster, and more persistent, than even an erlang call like :ets.</p><p><em>Originally published at </em><a href="https://prying.io/technical/2019/09/01/caching-options-in-an-elixir-application.html"><em>https://prying.io</em></a><em> on September 1, 2019.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f415149eda6b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using Benchee for Elixir Performance Tests]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@dkerrious/using-benchee-for-elixir-performance-tests-9cac8201f9aa?source=rss-1fc02e3021da------2"><img src="https://cdn-images-1.medium.com/max/2600/1*P4-Fn0hhOyR8sf8gfQRCFA.png" width="5114"></a></p><p class="medium-feed-snippet">Benchee is a useful tool if you find yourself in a situation where you need to test the limits of a system or need to define the behavior&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/@dkerrious/using-benchee-for-elixir-performance-tests-9cac8201f9aa?source=rss-1fc02e3021da------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@dkerrious/using-benchee-for-elixir-performance-tests-9cac8201f9aa?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/9cac8201f9aa</guid>
            <category><![CDATA[exunit]]></category>
            <category><![CDATA[benchmarking]]></category>
            <category><![CDATA[elixir]]></category>
            <category><![CDATA[erlang]]></category>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Sun, 21 Jul 2019 01:15:05 GMT</pubDate>
            <atom:updated>2019-07-25T15:08:19.186Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Array.prototype.sort() Behavior Change in Node v11]]></title>
            <link>https://medium.com/@dkerrious/array-prototype-sort-behavior-change-in-node-v11-3cd14b170ab5?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/3cd14b170ab5</guid>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[javascript-tips]]></category>
            <category><![CDATA[node]]></category>
            <category><![CDATA[reactjs]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Sat, 06 Apr 2019 01:15:05 GMT</pubDate>
            <atom:updated>2019-04-06T16:20:35.305Z</atom:updated>
            <content:encoded><![CDATA[<p>Several weeks ago, I started to experience random test failures in a previously stable part of an application. I started where any developer would start, asking myself if there were any changes recently which might have caused these issues. There was nothing, and <a href="https://github.com/nodejs/node/issues/24294">the real culprit</a> was not even on my radar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/369/1*phnK8E-V-NR_cHIovPtuyA.png" /></figure><p>Between Node v10 and Node v11, the behavior of Array.prototype.sort() changed. Previously in Node v10, this method accepted a boolean as a return value. Starting in Node v11, functions returning a boolean will not sort an array as expected.</p><p>Here is a <a href="http://dev.topheman.com/install-nvm-with-homebrew-to-use-multiple-versions-of-node-and-iojs-easily/">guide for setting up </a><a href="http://dev.topheman.com/install-nvm-with-homebrew-to-use-multiple-versions-of-node-and-iojs-easily/">nvm on macOS</a> if you would like to play around with this for yourself.</p><h3>Simple Demonstration of Changes</h3><p>In Node v10:</p><pre>var array = [ 3, 2, 1 ];<br>array.sort((a,b) =&gt; a &gt; b);<br>[ 1, 2, 3 ]</pre><p>In Node v11:</p><pre>var array = [ 3, 2, 1 ];<br>array.sort((a,b) =&gt; a &gt; b);<br>[ 3, 2, 1 ]</pre><pre>// instead, return a 0, negative, or positive value<br>array.sort((a, b) =&gt; a - b);<br>[ 1, 2, 3 ]</pre><h3>What I Learned</h3><p>After seeing this, I had to go back and check on some other projects which had the same misuse of the Array.prototype.sort() method. I realized through this experience that it is my job as a developer to be proactive. Being proactive as a developer means staying on top of the latest information relating to technologies I am using as well as making an effort to get in front of those potential problems in applications.</p><p><em>Originally published at </em><a href="https://prying.io/technical/2019/04/06/array-sort-behavior-change.html"><em>prying.io</em></a><em> on April 6, 2019.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3cd14b170ab5" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting Started With Redux]]></title>
            <link>https://medium.com/@dkerrious/getting-started-with-redux-fac2d6953cb7?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/fac2d6953cb7</guid>
            <category><![CDATA[react-redux]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[redux]]></category>
            <category><![CDATA[reactjs]]></category>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Thu, 20 Dec 2018 01:22:40 GMT</pubDate>
            <atom:updated>2018-12-20T01:22:40.365Z</atom:updated>
            <content:encoded><![CDATA[<p>I found <a href="https://redux.js.org/">Redux</a> a bit intimidating. There are actions, reducers, types, and you have to connect the store to your components. Oh, and don’t forget to implement middleware. It can be difficult to debug across so many different components, especially during set-up.</p><p>In my opinion, Redux is overkill for most projects. You can create a simple store through a root component’s state that is far easier to manage, understand, and implement.</p><p>If you are building something that you intend to be a production application, it is probably worth implementing React/Redux. It makes debugging easy since you have a log of all actions and state changes. It makes testing easy since you have explicit methods managing state.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*meCFnZ5MK_7Fu1ogYfBvNQ.png" /></figure><p>The most important principle with Redux is to keep state flat. Avoid nesting objects when you can reference ID’s and create a separate store for an item. For example, if you have a company with has employees, this is the wrong way to represent your state.</p><pre>--company<br>	-id<br>	-name<br>	-employees: [ employee_1, employee_2 ]</pre><p>These nested objects can lead to <a href="https://medium.com/@dkerrious/copying-and-object-in-javascript-64ca048cd8c7">javascript reference errors</a>. The most common way to copy an object is with Object.assign({}, state, { changes }). Unfortunately, this creates reference issues for any employees since Object.assign only applies first level properties.</p><p>Instead, we can flatten our state and reference those employees by their id.</p><pre>--employees: [ employee__1, employee__2 ]<br>--company<br>	-id<br>	-name<br>	- employees: [1, 2]</pre><p>Now, we can import employees only into the components which need them. The more you can avoid passing extra data around, the better.</p><p>In order to get redux set-up and implemented, we need to follow 3 simple steps. First, install dependencies. Second, create a basic redux store. Third, access that basic store in your components.</p><h3>Install Dependencies</h3><pre>npm install --save redux  react-redux redux-thunk</pre><p>I would also recommend installing the Redux Devtools extension on whatever browser you use for development. It provides a visual representation of state as well as a log of events making debugging a breeze.</p><h3>Create a Basic Redux Store</h3><pre>mkdir store<br>touch store/configureStore.js</pre><pre>// in configureStore.js<br>// import redux methods, compose is used to combine redux<br>// applyMiddleware implements redux-thunk<br>import {createStore, compose, applyMiddleware} from &#39;redux&#39;;<br>import rootReducer from &#39;../reducers/rootReducer&#39;;<br>import thunk from &#39;redux-thunk&#39;;</pre><pre>export default function configureStore() {<br>  return createStore(<br>    rootReducer,<br>    compose(<br>      applyMiddleware(thunk),<br>      window.__REDUX_DEVTOOLS_EXTENSION__ &amp;&amp; window.__REDUX_DEVTOOLS_EXTENSION__(),<br>    )<br>  );</pre><p>compose from redux is necessary to add multiple libraries to your redux store, like middleware and dev tools. createStore used to take more than 2 values, so many tutorials online will not include compose. Latest versions require using compose. Redux-thunk will prevent async issues associated with a centralized data store.</p><p>Now, we need to create a rootReducer. As a rule, you should have a reducer for each thing in your application. For this example, we will only have a blogs reducer. The rootReducer will pull in a blogs store. The configureStore.js file will instantiate that store. Let’s create our reducers.</p><pre>mkdir reducers<br>touch reducers/rootReducer.js<br>touch reducers/blogReducer.js</pre><pre>// in rootReducer.js<br>import {combineReducers} from &#39;redux&#39;;<br>import blogs from &#39;./blogReducer&#39;;</pre><pre>const rootReducer = combineReducers({ blogs });</pre><pre>export default rootReducer;</pre><p>Now, we have to create the blogReducer.</p><pre>touch reducers/blogReducer</pre><pre>// in blogReducer.js<br>export default function blogs(state = [], action) {<br>  switch (action.type) {<br>    default:<br>      return state;<br>  }<br>}</pre><p>Nothing fancy here. Our default state is an empty array. All we are doing is returning in that default scenario. This is all we need to set-up a store with blogs: [].</p><p>Now, we need to pass that store into our root component. Up until now, everything we have been doing has been using redux or redux-thunk. Now, we will include react-redux in the fun. In your root component rendering your application, wrap your application in the Provider component.</p><pre>// in index.js<br>import React from &#39;react&#39;;<br>import { render } from &#39;react-dom&#39;;<br>import { Provider } from &#39;react-redux&#39;;<br>import App from &#39;./containers/App&#39;;<br>import configureStore from &#39;./store/configureStore&#39;;</pre><pre>const store = configureStore();</pre><pre>render(<br>  &lt;Provider store={store}&gt;<br>    &lt;App /&gt;<br>  &lt;/Provider&gt;,<br>  document.getElementById(&#39;app&#39;)<br>);</pre><p>If you start your application and open your redux dev tools you should see your blogs: [] store!</p><h3>The last step is connecting your redux store to a component.</h3><p>To connect our blogs store, we will use a sample SimpleTable component. Replace this with any component in your library.</p><pre>import { connect } from &#39;react-redux&#39;;<br>...<br>// at the bottom of SimpleTable.js</pre><pre>SimpleTable.propTypes = {<br>  ...<br>  blogs: PropTypes.array.isRequired,<br>  ...<br>};</pre><pre>function mapStateToProps({ blogs }) {<br>  return { blogs };<br>}</pre><pre>export default connect(mapStateToProps)(SimpleTable);</pre><p>You should now be able to access this.props.blogs from within your component. If you are unsure, console logs are your friend.</p><p>If you are also using React Router, I would advise configuring your routes and components inside an App.js component and setting up Redux in an index.js file.</p><p>I hope this helps someone get started! I will have a follow-up post about using actions and types.</p><p>Here is a <a href="https://prying.io/technical/2018/12/19/getting-started-with-redux.html">link to the original post</a> on <a href="https://prying.io/">prying.io</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fac2d6953cb7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Configuring ESLint with Webpack and React]]></title>
            <link>https://medium.com/@dkerrious/configuring-eslint-with-webpack-and-react-9aab685e8c4c?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/9aab685e8c4c</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[eslint]]></category>
            <category><![CDATA[webpack]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Tue, 11 Dec 2018 18:07:45 GMT</pubDate>
            <atom:updated>2018-12-11T18:08:45.598Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/240/1*-IjrMiJJqGwl7TLxQYPpWQ.png" /><figcaption>eslint logo</figcaption></figure><p><a href="https://eslint.org/">ESLint</a> is bumper lanes for your project. Text editor extensions are useful while coding, but ESLint can enforce norms accross developers and remove the unpleasant discussions about syntax. Agree to syntax norms once and move forward. If individuals have issues with particular rules, they can present a coherent argument as to why they want to change that rule.</p><pre>npm install --save-dev eslint babel-eslint eslint-plugin-react eslint-watch</pre><p>Next, lets create a basic .eslintrc file in the root of your directory. Eslint will work without this, but I would encourage customizing rules for your project. Here is a basic config file for react@16.</p><pre>{<br>    &quot;plugins&quot;: [<br>        &quot;react&quot;<br>    ],<br>    &quot;parser&quot;: &quot;babel-eslint&quot;,<br>    &quot;parserOptions&quot;: {<br>        &quot;ecmaVersion&quot;: 6,<br>        &quot;sourceType&quot;: &quot;module&quot;,<br>        &quot;ecmaFeatures&quot;: {<br>            &quot;jsx&quot;: true<br>        }<br>    },<br>    &quot;env&quot;: {<br>        &quot;es6&quot;:     true,<br>        &quot;browser&quot;: true,<br>        &quot;node&quot;:    true,<br>        &quot;mocha&quot;:   true<br>    },<br>    &quot;extends&quot;: [<br>        &quot;eslint:recommended&quot;,<br>        &quot;plugin:react/recommended&quot;<br>    ],<br>    &quot;rules&quot;: {<br>      &quot;semi&quot;: [2, &quot;always&quot;],<br>      &quot;indent&quot;: [&quot;error&quot;, 2],<br>      &quot;object-curly-spacing&quot;: [&quot;error&quot;, &quot;always&quot;],<br>      &quot;no-extra-parens&quot;: &quot;error&quot;,<br>      &quot;max-len&quot;: [&quot;error&quot;, { &quot;code&quot;: 100 }],<br>      &quot;no-multi-spaces&quot;: &quot;error&quot;<br>    },<br>    &quot;settings&quot;: {<br>      &quot;react&quot;: {<br>        &quot;version&quot;: &quot;16.3&quot;<br>      }<br>    }<br>}</pre><h3>Running the linter</h3><p>The linter can be run against a single file using the node_module binary.</p><pre>./node_modules/eslint/bin/eslint.js index.js</pre><p>It is better to configure NPM scripts to do this for you. Here are what mine look like for a basic react starter project.</p><pre>// in package.json under scripts<br>...<br>&quot;lint&quot;: &quot;eslint ./index.js ./containers/*.js ./components/*.js&quot;,<br>&quot;lint:fix&quot;: &quot;eslint --fix ./index.js ./containers/*.js ./components/*.js&quot;,<br>...</pre><p>A few notes on the syntax there. The *.js refers to any file in that directory with a .js extension. If I had folders with components in my /components folder, I could test all folders and files within components like this ./components/**/*.js.</p><pre>...<br>&quot;lint&quot;: &quot;eslint ./index.js ./containers/*.js ./components/**/*.js&quot;,<br>&quot;lint:fix&quot;: &quot;eslint --fix ./index.js ./containers/*.js ./components/**/*.js&quot;,<br>...</pre><p>The fix option is your best friend. It will fix basic linter errors for you. Anyone who has had to do this manually for basic rules will appreciate what a help this is.</p><h3>Example Outputs</h3><p>If this ran correctly, you should see an output like the following.</p><pre>&gt; eslint ./index.js ./containers/*.js ./components/*.js<br><br><br>/home/david/blogbank.com/components/EnhancedTable.js<br>   65:4   error  Unnecessary semicolon                          no-extra-semi<br>  126:1   error  Expected indentation of 8 spaces but found 10  indent<br>  127:1   error  Expected indentation of 8 spaces but found 10  indent<br>  128:1   error  Expected indentation of 6 spaces but found 8   indent<br>  130:1   error  Expected indentation of 8 spaces but found 10  indent<br>  131:1   error  Expected indentation of 8 spaces but found 10  indent<br>  132:1   error  Expected indentation of 6 spaces but found 8   indent<br>  154:28  error  Unnecessary parentheses around expression      no-extra-parens<br>  158:13  error  Unnecessary parentheses around expression      no-extra-parens<br>  166:28  error  Unnecessary parentheses around expression      no-extra-parens<br>  172:13  error  Unnecessary parentheses around expression      no-extra-parens<br>  313:33  error  Unnecessary parentheses around expression      no-extra-parens<br><br>/home/david/blogbank.com/components/SearchAppBar.js<br>   5:8  error  &#39;IconButton&#39; is defined but never used  no-unused-vars<br>  10:8  error  &#39;MenuIcon&#39; is defined but never used    no-unused-vars<br><br>/home/david/blogbank.com/components/SimpleTable.js<br>  24:5  error  &#39;id&#39; is assigned a value but never used  no-unused-vars<br><br>✖ 15 problems (15 errors, 0 warnings)<br>  12 errors and 0 warnings potentially fixable with the `--fix` option.</pre><p>Now, we can fix those errors using our npm run lint:fix script. Without doing anything, 12/15 errors are auto-fixed.</p><pre>&gt; eslint --fix ./index.js ./containers/*.js ./components/*.js<br><br><br>/home/david/blogbank.com/components/SearchAppBar.js<br>   5:8  error  &#39;IconButton&#39; is defined but never used  no-unused-vars<br>  10:8  error  &#39;MenuIcon&#39; is defined but never used    no-unused-vars<br><br>/home/david/blogbank.com/components/SimpleTable.js<br>  24:5  error  &#39;id&#39; is assigned a value but never used  no-unused-vars<br><br>✖ 3 problems (3 errors, 0 warnings)</pre><p>That is it! Go forth and play nicely with other developers instead of arguing about syntax!</p><p>Here is a link to the <a href="http://prying.io/technical/2018/12/06/setting-up-eslint.html">original content</a> on <a href="http://prying.io/">prying.io</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9aab685e8c4c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting Up a Jekyll Blog]]></title>
            <link>https://medium.com/@dkerrious/setting-up-a-jekyll-blog-472772df2e5c?source=rss-1fc02e3021da------2</link>
            <guid isPermaLink="false">https://medium.com/p/472772df2e5c</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[ruby]]></category>
            <category><![CDATA[jekyll]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[blogging]]></category>
            <dc:creator><![CDATA[David Kerr]]></dc:creator>
            <pubDate>Mon, 03 Dec 2018 17:59:02 GMT</pubDate>
            <atom:updated>2018-12-04T12:45:24.310Z</atom:updated>
            <content:encoded><![CDATA[<p>I have been putting off the small chore of moving blog content from Medium. I was curious about some of the basic, markdown based content management systems out there and decided to use Jekyll because of my familiarity with GitHub Pages.</p><p>I am a big believer in not over-engineering solutions to problems. Before spending money on a website, check to see if a <a href="https://pages.github.com/">free option through Github</a> would suffice.</p><p>Markdown syntax is accessible to less technical users. Everything else is just configuration.</p><p>To get started, install Ruby, Bundler, and other dependencies until your terminal doesn’t scream at you when you follow <a href="https://jekyllrb.com/tutorials/home/">the Jekyll tutorial</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/498/1*I4F3HqaclaYo_I4Ey5alxA.png" /></figure><h3>Choosing a Theme</h3><p>Themes aren’t as easy to swap in and out as you are led to believe. Each has different layouts they provide. To minimize configuration, start with a theme which works for you. Here is a list of <a href="https://pages.github.com/themes/">themes supported by GitHub pages</a>.</p><p>There are two factors to consider when picking a theme. First, does the navigation suit your needs? For a blog, you would want a landing page which showed previews of your latest blog. For a simple page for a software download, you want the landing page to be a page. Think about your navigation and URL structure. Second, does the theme offer all of the layouts you need? Slate, for example, doesn’t have a ‘home’ layout. A site which worked on ‘minima’ won’t necessarily work with another theme. What matters is that a theme offers various layouts for flexibility.</p><p>When you create a page in markdown, you put metadata at the top about how you want Jekyll to handle the page.</p><pre> — -<br>layout: post<br>title: “Setting Up a Jekyll Blog”<br>date: 2018–12–03 18:15:05 -0700<br>categories: technical<br> — -</pre><p>The layout referenced must exist in your `_layouts` directory. Themes provided through gems will hide these layouts.</p><p>To overwrite them:</p><ul><li>create your own `_layouts` directory in the root of your project</li><li>copy and paste the file from the source code</li><li>make edits</li></ul><h3>Customize your CSS</h3><p>To avoid looking too basic, take a moment to customize your CSS. Choose a different font, change a header color, anything to make your site look less than boilerplate. Simple is good. Too simple is lazy. Configuring CSS is the same in concept as customizing layouts.</p><p>To configure CSS for most themes:<br>* create your own `assets` directory in the root of your project<br>* create a file `main.scss`, ideally just importing your theme<br>* make edits</p><pre> — -<br># Only the main Sass file needs front matter (the dashes are enough)<br> — -</pre><pre><a href="http://twitter.com/import">@import</a> “minima”;</pre><pre>.site-header {<br> background-color: red;<br>}</pre><pre>.site-title, .page-link {<br> color: white !important;<br>}</pre><h3>Adding Additional Pages</h3><p>To add additional pages to your root navigation, create files in the root of your project. The `about.markdown` file in the root of most themes is an example of this working.</p><p>Jekyll is made for rendering simple pages. It is capable of much more, but spending too much time trying to make it work for your specific need is a mistake. In my opinion, Jekyll should be used for standard site templates; blogs, personal sites, and informational pages.</p><h3>Other Notes</h3><p>Starting Jekyll should be as simple as `bundle exec jekyll serve`.</p><p>Depending on your machine, you might have to configure the port in the `_config.yml` file. The default is port 4000.</p><pre>port:3999</pre><p>Some configuration is done in `_layouts`, some configuration is done in `_includes`. The `_includes` directory contains reusable HTML fragments which are *included* in layouts.</p><p>If you want to update your `header.html`, for example, you would:</p><ul><li>copy the source file from your theme into the `_includes` directory</li><li>start editing</li></ul><p>In summation, my experience getting started with Jekyll was a positive one. Github has stellar documentation for pages, themes, and Jekyll. Jekyll’s documentation is slightly more cryptic but also helpful for understanding the basic concepts of the framework.</p><p>Here is <a href="http://prying.io/technical/2018/12/03/setting-up-jekyll-blog.html">a link</a> to the original post on <a href="http://prying.io/">prying.io</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=472772df2e5c" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>