Everyone in Boulder has a dog. Everyone in Boulder who has a dog and works at MojoTech brings their dog to work. However, not everyone that has a dog works at MojoTech, leading to a need for dog walkers and ultimately an application to schedule outings.
Later, in Phase 2, we’ll configure Webpack to use hot-loading (for happy development) and code-splitting (for performance). For now the app is small, we’re not noticing any problems bundling all of our React code with Browserify, so we’ll keep the configuration simple.
Our first component is a Bootstrap React Datepicker, to use inside a Rails form. Because we are setting the state within the component, it’s considered a Controlled Component (or ‘smart’ component).
The datepicker we used may be cloned from https://github.com/quri/react-bootstrap-datetimepicker.git. There are also several forks of this original project, which was in turn a port of https://github.com/Eonasdan/bootstrap-datetimepicker for React.js. We chose to use the original fork in order to add timezone functionality without modifying the plugin.
Step 1) Rails set-up
Set up your Rails project, and brew install node.js if necessary (on macs). Add
gem ‘browserify-rails’ to your gemfile. From the command line install the necessary dependencies.
npm init to insert a package.json in the root directory. Then run
npm install moment moment-timezone --save npm install react-bootstrap-datetimepicker --save
Our Datepicker component is written using ES6 syntax, so we’ll need to add additional dependencies to transpile the code.
npm install babelify eslint babel-eslint --save npm install eslint-plugin-react --save-dev
For your reference, the Dogwalker code is available at https://github.com/mojotech/dogwalker. If you’d like to follow along you may clone the repo, set up your preferred db (we’re using postgresql) and run
bundle && npm install. You should be good to go.
In the rails form we’ll replace the usual text_field helper used with jquery datepickers with
<div class=”datepicker-initializer”></div>. Passing information to the React component via data-attributes allows the component code to be re-usable. For our purposes we want to pass in:
- The Rails model (data-model)
- The form label name (data-label)
- The form field (data-field)
- The date and time to be edited, or other date/time (data-date).
- A specific timezone (data-timezone) -- someone could be traveling to Boulder with their Miniature Schnauzer.
In the _form partial, the code now resembles:
<div class="datepicker-initializer" data-model= "daily_schedule" data-field= "date_and_time" data-label= "Start Time" data-timezone= <%= @dog_walker.time_zone %> data-date= <%= @daily_schedule.start_time %>> </div>
By default the datepicker uses the timezone of the browser, which could be confusing in some cases. Consequently, dog walkers are able to set the timezone for their location in their user profile, which is what we’re passing as
Add some organizational structure:
and in turn requires the bundled (transpiled, browserified) react js.jsx files:
app.js.jsx is the main application “root” for all of the React code, and this is where we’ve put our initialization code. If we were to use Redux, we would also add the
<Provider> inside this file.
Since we will probably use
moment formatting in several areas of the application, we’ll declare the time formats as constants in a separate directory and then import them wherever needed:
The app.js file references the Datepicker Component, passing down the data-attributes as props. Now all we need is to add that component to datepicker.js.jsx (inside the components folder). In addition to the render function we’ll also add an initial state, and a handler for setting the state, when changed.
The datetimepicker plugin takes several props, including
inputProps (an object literal containing any extra information), and
defaultText (used only in the initial render).
dateInZone() return dates formatted to the passed-in timezone, if it exists. Otherwise they will return the time based on the browser’s timezone.
Now whenever a new date/time is selected, the onChange function is triggered, which calls the handleChange function, which in turn displays the date and time inside the form field. The field value is also simultaneously updated in a format which can be interpreted correctly by Rails.
There are a few gotchas to be aware of when using the react-bootstrap-datetimepicker module.
- In the initial render, the
defaultTextprop is used to display the initial date passed in by Rails. It is not used in subsequent renders.
- The plugin uses moment.js in strict mode, which means it’s imperative to match the format used for setting the new value to the
- The plugin converts datetimes to timestamps with milliseconds. In order to format the date properly for Rails, we needed to convert to a unix timestamp in seconds.
- It’s necessary to use
handleOnChangefunction in order to update the form field’s value as well as the date to display.
If you’ve noticed, we haven’t used any React Lifecycle methods such as
ComponentWillMount in our code. But in fact the React Lifecycle is maintained; these methods are part of the
DateTimeField Class, which lives inside the module. And there you have it, a simple re-usable React Datetimepicker.
It’s almost magic. Except it’s not. It’s React!