Today I Learned

hashrocket A Hashrocket project

12 posts by ryanmessner

HTML Tab Index Gotcha

The tabindex attribute on HTML elements behaves differently than one might first expect.

The default tabindex for an element is 0, all elements with this index will be tabbed to in the order they appear in the document.

Giving an element a positive tabindex will prioritize it over all elements with a 0 index, in the order they appear.

Consider the following code

<ul>
  {
    [0,1,2].map((n) => (
      <li><button tabindex={n}>{n}</button></li>
    )
  }
</ul>
React JSX

The order that the buttons will be focused when tabbing is 1, then 2, and finally 0.

Simulate componentDidMount with a useEffect

If you want to simulate a componentDidMount class method inside your functional component, you can use the useEffect hook thusly:

const MyFunctionalComponent = () => {
   useEffect(() => {
   
     // any code here is run once 
     
   }, []); 
   
   // the empty array will be the same each 
   // time the component is called, so the useEffect
   // will not run in calls subsequent to the first
   
   return <div>Foo</div>;
}
React JSX

ht: @greis

Pattern matching against Dates in Elixir

Dates in Elixir aren't native values, so there aren't any guard clause functions available for use with date/datetime. You can, however, use pattern matching:

def foo(%Date{} = date) do
  Timex.to_naive_datetime(date) |> foo
end

def foo(%DateTime{} = datetime) do
  Timex.to_naive_datetime(datetime) |> foo
end

def foo(%NaiveDateTime{} = datetime) do
  IO.inspect({"My Naive Datetime", datetime})
end
Elixir

Send an event to a Channel from outside Phoenix

So this one was non-obvious to me.

In the following example, any module on any process can call TopicChannel.send_to_channel/1and that will be handled by the handle_info call below and sent to the socket.

defmodule MyAppWeb.TopicChannel do
  use Phoenix.Channel

  def send_to_channel(data) do
    Phoenix.PubSub.broadcast(
      MyApp.PubSub, 
      "topic:subtopic",
      %{type: "action", payload: %{data: data}
    )
  end

  def join("topic:subtopic", message, socket) do
    {:ok, socket}
  end
  
  def handle_info(%{type: "action"}=info, socket) do
    push socket, "action", info

    {:noreply, socket}
  end
end
Elixir

Speed up webpacker by excluding dev dependencies

In our latest project, we were experiencing some long build times after accreting features for a few monhts.

By default webpacker pulls in all of your node_modules for parsing and optimization by babel, which can be quite unneccessary. In order to exclude your devDependencies from that process, you can add the folling code to your development.js config:

...

var package = require('../../package.json');
var excluded = Object.keys(package.devDependencies).map(function(dep){
  return new RegExp("node_modules/" + dep);
});

module.exports = merge(environment.toWebpackConfig(), customConfig, {
  module: {
    noParse: excluded
  }
});
JavaScript

The noParse option can possibly lead to errors when some packages are excluded from parsing (notably, css-loader), you can tweak which dependencies reside in dependencies vs devDependencies in your package.json in order to avoid these issues.

Share SCSS Variables with Javascript

When working with React and Sass, sometimes you want to be able to share variables defined in the stylesheets with your React javascript code. This is actually pretty simple with Webpack.

First make sure webpack is set up to import scss:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{
      test: /\.scss$/,
      use: [{
        loader: "style-loader" // creates style nodes from JS strings
      }, {
        loader: "css-loader" // translates CSS into CommonJS
      }, {
        loader: "sass-loader" // compiles Sass to CSS
      }]
    }]
  }
};
JavaScript

Then define your variables export file:

// variables.scss
$white-color: #fcf5ed;
$dark-color: #402f2b;
$light-color: #e6d5c3;
$medium-color: #977978;
$alert-color: #cb492a;
$light-black-color: #706e72;
$black-color: #414042;

// the :export directive is the magic sauce for webpack
:export {
  whitecolor: $white-color;
  darkcolor: $dark-color;
  lightcolor: $light-color;
  mediumcolor: $medium-color;
  alertcolor: $alert-color;
  lightblackcolor: $light-black-color;
  blackcolor: $black-color;
}
CSS

Now you're ready to import your variables to use with your React components:

import variables from 'variables.scss';

const CSS = {
  backgroundColor: variables.blackcolor
}

export default ({}) => {
  return <div style={CSS}>Content</div>
}
JavaScript

Delayed Job Queue Adapter in RSpec with Rails 5.1

In old versions of Rails, you were able to override the ActiveJob queue in a test like this:

describe MyJob do
  it 'works' do
    ActiveJob::Base.queue_adapter = :delayed_job
    expect {
      MyJob.perform_later(some_params)
    }.to change(Delayed::Job.count).by(1)
  end
end
Ruby

With Rails 5.1, we have the ActiveJob::TestHelper class which you will need to employ in your tests. In order to override the queue a different strategy is needed.

describe MyJob do
  def queue_adapter_for_test
    ActiveJob::QueueAdapters::DelayedJobAdapter.new
  end
  
  it 'works' do
    expect {
      MyJob.perform_later(some_params)
    }.to change(Delayed::Job.count).by(1)
  end
end
Ruby

You will need to add the following to your rspec config or a support file:

RSpec.configure do |config|
  config.include(ActiveJob::TestHelper)
end

# you will also need the code below for the test
# to clear out the jobs between test runs
class ActiveJob::QueueAdapters::DelayedJobAdapter
  class EnqueuedJobs
    def clear
      Delayed::Job.where(failed_at:nil).map &:destroy
    end
  end
  
  class PerformedJobs
    def clear
      Delayed::Job.where.not(failed_at:nil).map &:destroy
    end
  end
  
  def enqueued_jobs
    EnqueuedJobs.new
  end
  
  def performed_jobs
    PerformedJobs.new
  end
end
Ruby

Firefox DevTools Vim Mode

I've been using Firefox more and more lately, and keep discovering new features with the devtools. One of the most handy when trying to edit css or html is vim mode.

To enable vim mode in the devtools, first to to your about:config page:

Change the devtools.editor.keymap setting to vim and you get all your familiar editing functionality.

Use Decorators for React Higher Order Components

When working with React libraries that make use of higher order components, you wrap your component in a function:

import { withRouter } from 'react-router-dom';

class MyComponent extends Component {
  ...
}

export default withRouter(MyComponent)
JavaScript

When chaining through multiple HOC's, this can become unwieldly:

import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';

class MyComponent extends Component {
  ...
}

function mapStateToProps(state) { ... };

export default withRouter(connect(mapStateToProps)(MyComponent));
JavaScript

But there is hope, with the decorator syntax in ES6/Babel you can make this look a bit cleaner:

import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';

function mapStateToProps(state) { ... };

@withRouter
@connect(mapStateToProps)
export default class MyComponent extends Component {
  ...
}
JavaScript

Full documentation here.

Alternate names in destructuring assignment

Want to use destructuring assignment, but don't like the names of the variables as they exist in the object?

If you have data like this:

const optimus = { name: 'Optimus', type: 'Semi-Truck' };
const bumblebee = { name: 'Bumblebee', type: 'Small Car' };
JavaScript

Instead of creating customized variables like this:

const { name, type } = bumblebee;
const bumblebeeName = name;
const bumblebeeType = type;
JavaScript

You can express this type of destructuring in one line like:

const { name: optimusName, type: optimusType } = optimus;
JavaScript