science and technology Walking around San Francisco on July 4th By nicolasgallagher.com Published On :: Sat, 06 Jul 2013 17:00:00 -0700 For the first time since coming back to San Francisco in January, I had everything I needed for a saunter across the city in the sun: a means of taking photos / videos, a pair of sunglasses, no work, no plans, and no excuse. On the morning of July 4th, I decided to spend the next couple of days offline. I read a book, and decided to go for a walk the rest of the day. I didn’t have any expectations or intended destination. I left my apartment at 2pm and decided to walk west, as I haven’t spent any proper time on that side of the city. I passed through a couple of small parks and quiet neighbourhoods before hitting the edge of The Presidio. At this point, I realised how long it had been since I’d seen a large expanse of something approximating nature. The Presidio was beautifully tranquil, with just a handful of people strolling or running through the trees. Walking off the trails, I saw a lizard for the first time in years; probably a San Francisco Alligator Lizard. I exited The Presidio somewhere near the golf course and picked a long road to keep walking west. On the way, I hit a main road and stood at the traffic lights. While I waited a young woman walking her dog struck up the first of several impromptu conversations I had with strangers that day. She must have seen me looking around for the street name, as she asked, “Are you lost? Are you a tourist? Where are you going?” “I’m not sure. That way”, I said pointing down the long road before us. She laughed. “See, you are lost!” We chatted for a few blocks before our paths diverged. She told me that I would find some nice trails, and a good view of the Golden Gate Bridge, in the woodland near Lands End. It was dead ahead for another 30 minutes. So that’s where I went. I hit the trails at about 4:30pm. It must have been close to perfect weather. Really sunny, warm, only a mild breeze, and the bay was completely clear. I wandered around for over an hour; perching near the edge of cliffs, taking in the sight of the Golden Gate Bridge on my right and a vast expanse of ocean to my left. Such a relaxing place. I made time for a Dorsey-like Vine (my first Vine)… On the way back, I crossed a road to take a photo. A post-middle-age man crossed my path, struck up a conversation, and began to tell me about his life in San Francisco “back in the day”. As if he could peer into my soul, he assured me that there was nothing wrong with being a software engineer (although he did initially think I was an estate agent; that was one of the first things he said to me). My spirits further lifted by a stranger’s validation, I continued home. For last 30 minutes all I could think about was lying down, resting my feet, and eating. I’d been walking for nearly 6 hours. I’ll definitely do it again, but a skateboard would be helpful next time. Full Article
science and technology A simple Git deployment strategy for static sites By nicolasgallagher.com Published On :: Wed, 01 Jan 2014 16:00:00 -0800 This is how I am deploying the build of my static website to staging and production domains. It requires basic use of the CLI, Git, and SSH. But once you’re set up, a single command will build and deploy. TL;DR: Push the static build to a remote, bare repository that has a detached working directory (on the same server). A post-receive hook checks out the files in the public directory. Prerequisites A remote web server to host your site. SSH access to your remote server. Git installed on your remote server (check with git --version). Generate an SSH key if you need one. On the server Set up password-less SSH access First, you need to SSH into your server, and provide the password if prompted. ssh user@hostname If there is no ~/.ssh directory in your user’s home directory, create one: mkdir ~/.ssh. Next, you need to copy your public SSH key (see “Generate an SSH key” above) to the server. This allows you to connect via SSH without having to enter a password each time. From your local machine – assuming your public key can be found at ~/.ssh/id_rsa.pub – enter the following command, with the correct user and hostname. It will append your public key to the authorized_keys file on the remote server. ssh user@hostname 'cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub If you close the connection, and then attempt to establish SSH access, you should no longer be prompted for a password. Create the remote directories You need to have 2 directories for each domain you want to host. One for the Git repository, and one to contain the checked out build. For example, if your domain were example.com and you also wanted a staging environment, you’d create these directories on the server: mkdir ~/example.com ~/example.git mkdir ~/staging.example.com ~/staging.example.git Initialize the bare Git repository Create a bare Git repository on the server. This is where you will push the build assets to, from your local machine. But you don’t want the files served here, which is why it’s a bare repository. cd ~/example.git git init --bare Repeat this step for the staging domain, if you want. Write a post-receive hook A post-receive hook allows you to run commands after the Git repository has received commits. In this case, you can use it to change Git’s working directory from example.git to example.com, and check out a copy of the build into the example.com directory. The location of the working directory can be set on a per-command basis using GIT_WORK_TREE, one of Git’s environment variables, or the --work-tree option. cat > hooks/post-receive #!/bin/sh WEB_DIR=/path/to/example.com # remove any untracked files and directories git --work-tree=${WEB_DIR} clean -fd # force checkout of the latest deploy git --work-tree=${WEB_DIR} checkout --force Make sure the file permissions on the hook are correct. chmod +x hooks/post-receive If you need to exclude some files from being cleaned out by Git (e.g., a .htpasswd file), you can do that using the --exclude option. This requires Git 1.7.3 or above to be installed on your server. git --work-tree=${WEB_DIR} clean -fd --exclude=<pattern> Repeat this step for the staging domain, if you want. On your local machine Now that the server configuration is complete, you want to deploy the build assets (not the source code) for the static site. The build and deploy tasks I’m using a Makefile, but use whatever you feel comfortable with. What follows is the basic workflow I wanted to automate. Build the production version of the static site. make build Initialize a new Git repo in the build directory. I don’t want to try and merge the new build into previous deploys, especially for the staging domain. git init ./build Add the remote to use for the deploy. cd ./build git remote add origin ssh://user@hostname/~/example.git Commit everything in the build repo. cd ./build git add -A git commit -m "Release" Force-replace the remote master branch, creating it if missing. cd ./build git push -f origin +master:refs/heads/master Tag the checked-out commit SHA in the source repo, so I can see which SHA’s were last deployed. git tag -f production Using a Makefile: BUILD_DIR := ./build STAGING_REPO = ssh://user@hostname/~/staging.example.git PROD_REPO = ssh://user@hostname/~/example.git install: npm install # Deploy tasks staging: build git-staging deploy @ git tag -f staging @ echo "Staging deploy complete" prod: build git-prod deploy @ git tag -f production @ echo "Production deploy complete" # Build tasks build: clean # whatever your build step is # Sub-tasks clean: @ rm -rf $(BUILD_DIR) git-prod: @ cd $(BUILD_DIR) && git init && git remote add origin $(PROD_REPO) git-staging: @ cd $(BUILD_DIR) && git init && git remote add origin $(STAGING_REPO) deploy: @ cd $(BUILD_DIR) && git add -A && git commit -m "Release" && git push -f origin +master:refs/heads/master .PHONY: install build clean deploy git-prod git-staging prod staging To deploy to staging: make staging To deploy to production: make prod Using Make, it’s a little bit more hairy than usual to force push to master, because the cd commands take place in a sub-process. You have to make sure subsequent commands are on the same line. For example, the deploy task would force push to your source code’s remote master branch if you failed to join the commands with && or ;! I push my site’s source code to a private repository on BitBucket. One of the nice things about BitBucket is that it gives you the option to prevent deletions or history re-writes of branches. If you have any suggested improvements, let me know on Twitter. Full Article
science and technology Flexible CSS cover images By nicolasgallagher.com Published On :: Tue, 07 Jan 2014 16:00:00 -0800 I recently included the option to add a large cover image, like the one above, to my posts. The source image is cropped, and below specific maximum dimensions it’s displayed at a predetermined aspect ratio. This post describes the implementation. Demo: Flexible CSS cover images Known support: Chrome, Firefox, Safari, Opera, IE 9+ Features The way that the cover image scales, and changes aspect ratio, is illustrated in the following diagram. The cover image component must: render at a fixed aspect ratio, unless specific maximum dimensions are exceeded; support different aspect ratios; support max-height and max-width; support different background images; display the image to either fill, or be contained within the component; center the image. Aspect ratio The aspect ratio of an empty, block-level element can be controlled by setting a percentage value for its padding-bottom or padding-top. Given a declaration of padding-bottom:50% (and no explicit height), the rendered height of the element will be 50% of its width. .CoverImage { padding-bottom: 50%; } Changing that padding value will change the aspect ratio. For example, padding of 25% results in an aspect ratio of 4:1, padding of 33.333% results in an aspect ratio of 3:1, etc. Maximum dimensions The problem with using this aspect ratio hack is that if the element has a max-height declared, it will not be respected. To get around this, the hack can be applied to a pseudo-element instead. .CoverImage:before { content: ""; display: block; padding-bottom: 50%; } Now the main element can take a max-height. It should also clip the pseudo-element overflow. .CoverImage { display: block; max-height: 300px; max-width: 1000px; overflow: hidden; } .CoverImage:before { content: ""; display: block; padding-bottom: 50%; } This aspect ratio pattern is provided by the FlexEmbed component for SUITCSS. That component is primarily for responsive video embeds, but it’s flexible enough to be useful whenever you need an element rendered at a predetermined aspect ratio. It comes with modifiers for 2:1, 3:1, 16:9, and 4:3 aspect ratios. The cover image component can extend the FlexEmbed component. <div class="CoverImage FlexEmbed FlexEmbed--2by1"></div> Background image The cover image is applied as a background image that is sized to cover the entire area of the element. This makes sure the image is clipped to fit the aspect ratio of the element. .CoverImage { ... background-repeat: no-repeat; background-size: cover; } If you want different cover images for different instances of the component, they can be applied via the style attribute. <div class="..." style="background-image: url(cover.jpg)"></div> The image can be fully centered by using background positioning and block centering. This makes sure that the image is centered in the element, and that the element is centered within its parent (when it reaches the max-width value). .CoverImage { ... background-position: 50%; background-repeat: no-repeat; background-size: cover; margin: 0 auto; } Final result If you depend on the FlexEmbed module, the amount of additional code required is minimal. (See the demo for all the code, including the FlexEmbed dependency.) /** * Requires: suitcss/flex-embed */ .CoverImage { background-position: 50%; background-repeat: no-repeat; background-size: cover; margin: 0 auto; max-height: 300px; max-width: 1000px; } <div class="CoverImage FlexEmbed FlexEmbed--3by1" style="background-image:url(cover.jpg)"> </div> You can add further customizations, such as setting the accompanying background color, or providing a means to switch between the cover and contain keywords for background-size. Full Article
science and technology Custom CSS preprocessing By nicolasgallagher.com Published On :: Wed, 12 Mar 2014 17:00:00 -0700 Did you know that you can build your own CSS preprocessor with Node.js libraries? They can be used alongside established preprocessors like Sass, and are useful for defining tasks beyond preprocessing. Libraries like Rework and PostCSS let you create and assemble an arbitrary collection of plugins that can inspect or manipulate CSS. At the time of writing, Twitter uses Rework to perform various tasks against our CSS source code for twitter.com. Creating a CSS preprocessor with Rework At its core, Rework is a module that accepts a string of CSS, produces a CSS abstract syntax tree (AST), and provides an API for manipulating that AST. Plugins are functions that have access to the AST and a Rework instance. Rework lets you chain together different plugins and generate a string of new CSS when you’re done. The source string is passed into the rework function and each plugin is applied with .use(fn). The plugins transform the data in the AST, and .toString() generates the new string of CSS. Below is an example of a custom preprocessor script using Rework and Autoprefixer. It’s a simplified version of the transformation step we use for twitter.com’s CSS. var autoprefixer = require('autoprefixer'); var calc = require('rework-calc'); var rework = require('rework'); var vars = require('rework-vars')(); var css = fs.readFileSync('./css/main.css', 'utf-8'); css = rework(css) .use(vars) .use(calc) .toString(); css = autoprefixer().process(css); fs.writeFileSync('./build/bundle.css', css) The script runs rework-vars, rework-calc, and then passes the CSS to Autoprefixer (which uses PostCSS internally) to handle the addition of any necessary vendor prefixes. rework-vars provides a limited subset of the features described in the W3C-style CSS custom property spec. It’s not a polyfill! Variables can be declared as custom CSS properties on the :root element, prefixed with --. Variables are referenced with the var() function, taking the name of a variable as the first argument and an optional fallback as the second. For example, this source: :root { --width-button: 200px; } .button { width: var(--width-button); } yields: .button { width: 200px; } There are many different Rework plugins that you can use to create a custom preprocessor. A more complete list is available on npm. In order to limit the chances of long-term divergence between our source code and native CSS, I’ve chosen to stick fairly closely to features that are aligned with future additions to native CSS. Creating your own Rework plugin Rework plugins are functions that inspect or mutate the AST they are provided. Below is a plugin that rewrites the value of any font-family property to sans-serif. module.exports = function plugin(ast, reworkInstance) { ast.rules.forEach(function (rule) { if (rule.type != 'rule') return; rule.declarations.forEach(function (declaration, index) { if (declaration.property == 'font-family') { declaration.value = 'sans-serif'; } }); }); }; Rework uses css-parse to create the AST. Unfortunately, both projects are currently lacking comprehensive documentation of the AST, but it’s not difficult to piece it together yourself. Beyond preprocessing Since Rework and PostCSS expose an AST and provide a plugin API, they can be used for other CSS tasks, not just preprocessing. At Twitter, our CSS build pipeline allows you to perform custom tasks at 2 stages of the process: on individual files and on generated bundles. We use Rework at both stages. Individual files are tested with rework-suit-conformance to ensure that the SUIT-style CSS for a component is properly scoped. /** @define MyComponent */ :root { --property-MyComponent: value; } .MyComponent {} Bundles are preprocessed as previously described, and also tested with rework-ie-limits to ensure that the number of selectors doesn’t exceed IE 8/9’s limit of 4095 selectors per style sheet. Other tasks you can perform include generating RTL style sheets (e.g., css-flip) and extracting detailed information about the perceived health of your CSS (e.g., the number of different colours used, duplicate selectors, etc.). Hopefully this has given you a small glimpse into some of the benefits and flexibility of using these tools to work with CSS. Full Article
science and technology How to test React components using Karma and webpack By nicolasgallagher.com Published On :: Sat, 16 May 2015 17:00:00 -0700 I’m working on a project at Twitter that uses React and webpack. After a few conversations with @sokra last year, this is the setup I put in place for testing React components (authored using JSX and ES6) using Karma. Dependencies You’ll need to install various packages. It looks like a lot of dependencies, but all the non-Karma packages will be necessary for general module bundling during development. Full set of required packages: babel-core: to transform JSX and transpile ES6. babel-loader: webpack loader for Babel. karma: the test runner. karma-chai-plugins: assertion library (or your library of choice). karma-chrome-launcher: Chrome browser launcher. karma-cli: CLI tool for Karma. karma-mocha: adapter for the Mocha testing framework. karma-sourcemap-loader: sourcemap loader. karma-webpack: to use webpack with Karma. react webpack: module bundler webpack entry file If you use webpack-specific features in your modules (e.g., loaders, plugins) you will need to use webpack to build a test bundle. The fastest and simplest approach is to create a single, test-specific entry file. Create a file named tests.bundle.js. Within this file, you create a webpack context to match all the files that conform to a naming pattern – in this case *.spec.js(x). var context = require.context('.', true, /.+.spec.jsx?$/); context.keys().forEach(context); module.exports = context; Next, you point Karma to this file. Karma config Karma is configured using a karma.conf.js file. The browsers, plugins, and frameworks are specified in the standard way. Point Karma at the tests.bundle.js file, and run it through the relevant preprocessor plugins (see example below). The karma-webpack plugin relies on 2 custom properties of the Karma config: webpack and webpackMiddleware. The value of the former must be a webpack config object. module.exports = function (config) { config.set({ browsers: [ 'Chrome' ], // karma only needs to know about the test bundle files: [ 'tests.bundle.js' ], frameworks: [ 'chai', 'mocha' ], plugins: [ 'karma-chrome-launcher', 'karma-chai', 'karma-mocha', 'karma-sourcemap-loader', 'karma-webpack', ], // run the bundle through the webpack and sourcemap plugins preprocessors: { 'tests.bundle.js': [ 'webpack', 'sourcemap' ] }, reporters: [ 'dots' ], singleRun: true, // webpack config object webpack: { devtool: 'inline-source-map', module: { loaders: [ { exclude: /node_modules/, loader: 'babel-loader, test: /.jsx?$/ } ], } }, webpackMiddleware: { noInfo: true, } }); }; Rather than duplicating your webpack config, you can require it in the Karma config file and override the devtool value to get sourcemaps working. var webpackConfig = require('./webpack.config'); webpackConfig.devtool = 'inline-source-map'; module.exports = function (config) { config.set({ ... webpack: webpackConfig }); }; That’s all you need to do to configure Karma to use webpack to load your JSX, ES6 React components. Full Article
science and technology Using canvas to fix SVG scaling in Internet Explorer By nicolasgallagher.com Published On :: Mon, 18 May 2015 17:00:00 -0700 Internet Explorer 9–11 suffer from various bugs that prevent proper scaling of inline SVG’s. This is particularly problematic for SVG icons with variable widths. This is the canvas-based hack I’ve been using to work around the issue. A popular way to use SVG icons is to generate a spritemap of SVG symbol‘s that you then reference from elsewhere in a document. Most articles on the topic assume your icon dimensions are uniformly square. Twitter’s SVG icons (crafted by @sofo) are variable width, to produce consistent horizontal whitespace around the vectors. Most browsers will preserve the intrinsic aspect ratio of an SVG. Ideally, I want to set a common height for all the icons (e.g., 1em), and let the browser scale the width of each icon proportionally. This also makes it easy to resize icons in particular contexts – just change the height. Unfortunately, IE 9–11 do not preserve the intrinsic aspect ratio of an inline SVG. The svg element will default to a width of 300px (the default for replaced content elements). This means it’s not easy to work with variable-width SVG icons. No amount of CSS hacking fixed the problem, so I looked elsewhere – and ended up using canvas. canvas and aspect ratios A canvas element – with height and width attributes set – will preserve its aspect ratio when one dimension is scaled. The example below sets a 3:1 aspect ratio. <canvas height="1" width="3"></canvas> You can then scale the canvas by changing either dimension in CSS. canvas { display: block; height: 2rem; } Demo: proportional scaling of canvas. Fixing SVG scaling in IE This makes canvas useful for creating aspect ratios. Since IE doesn’t preserve the intrinsic aspect ratio of SVG icons, you can use canvas as a shim. A canvas of the correct aspect ratio provides a scalable frame. The svg can then be positioned to fill the space created by this frame. The HTML is straightforward: <div class="Icon" role="img" aria-label="Twitter"> <canvas class="Icon-canvas" height="1" width="3"></canvas> <svg class="Icon-svg"> <use fill="currentcolor" xlink:href="#icon-twitter"></use> </svg> </div> So is the CSS: .Icon { display: inline-block; height: 1em; /* default icon height */ position: relative; user-select: none; } .Icon-canvas { display: block; height: 100%; visibility: hidden; } .Icon-svg { height: 100%; left: 0; position: absolute; top: 0; width: 100%; } Setting the canvas height to 100% means it will scale based on the height of the component’s root element – just as SVG’s do in non-IE browsers. Changing the height of the Icon element scales the inner SVG icon while preserving its 3:1 aspect ratio. Demo: proportional scaling of svg in IE. Creating an Icon component The hack is best added to (and eventually removed from) an existing icon component’s implementation. If you’re generating and inlining an SVG spritemap, you will need to extract the height and width (usually from viewBox) of each of your icons during the build step. If you’re already using the gulp-svgstore plugin, it supports extracting metadata. Those dimensions need to be set on the canvas element to produce the correct aspect ratio for a given icon. Example React component (built with webpack): import iconData from './lib/icons-data.json'; import React from 'react'; import './index.css'; class Icon extends React.Component { render() { const props = this.props; const height = iconData[props.name.height]; const width = iconData[props.name.width]; // React doesn't support namespaced attributes, so we have to set the // 'use' tag with innerHTML const useTag = `<use fill="currentcolor" xlink:href="#icon-${props.name}"> </use>`; return ( <span className="Icon"> <canvas className="Icon-canvas" height={height} width={width} /> <svg className="Icon-svg" dangerouslySetInnerHTML={{__html: useTag}} key={props.name} /> </span> ); } } export default Icon; When I introduced this hack to a code base at Twitter, it had no impact on the the rest of the team or the rest of the code base – one of the many benefits of a component-based UI. Full Article
science and technology Redux modules and code-splitting By nicolasgallagher.com Published On :: Thu, 01 Feb 2018 16:00:00 -0800 Twitter Lite uses Redux for state management and relies on code-splitting. However, Redux’s default API is not designed for applications that are incrementally-loaded during a user session. This post describes how I added support for incrementally loading the Redux modules in Twitter Lite. It’s relatively straight-forward and proven in production over several years. Redux modules Redux modules comprise of a reducer, actions, action creators, and selectors. Organizing redux code into self-contained modules makes it possible to create APIs that don’t involve directly referencing the internal state of a reducer – this makes refactoring and testing a lot easier. (More about the concept of redux modules.) Here’s an example of a small “redux module”. // data/notifications/index.js const initialState = []; let notificationId = 0; const createActionName = name => `app/notifications/${name}`; // reducer export default function reducer(state = initialState, action = {}) { switch (action.type) { case ADD_NOTIFICATION: return [...state, { ...action.payload, id: notificationId += 1 }]; case REMOVE_NOTIFICATION: return state.slice(1); default: return state; } } // selectors export const selectAllNotifications = state => state.notifications; export const selectNextNotification = state => state.notifications[0]; // actions export const ADD_NOTIFICATION = createActionName(ADD_NOTIFICATION); export const REMOVE_NOTIFICATION = createActionName(REMOVE_NOTIFICATION); // action creators export const addNotification = payload => ({ payload, type: ADD_NOTIFICATION }); export const removeNotification = () => ({ type: REMOVE_NOTIFICATION }); This module can be used to add and select notifications. Here’s an example of how it can be used to provide props to a React component. // components/NotificationView/connect.js import { connect } from 'react-redux'; import { createStructuredSelector } from 'reselect'; import { removeNotification, selectNextNotification } from '../../data/notifications'; const mapStateToProps = createStructuredSelector({ nextNotification: selectNextNotification }); const mapDispatchToProps = { removeNotification }; export default connect(mapStateToProps, mapDispatchToProps); // components/NotificationView/index.js import connect from './connect'; export class NotificationView extends React.Component { /*...*/ } export default connect(NotificationView); This allows you to import specific modules that are responsible for modifying and querying specific parts of the overall state. This can be very useful when relying on code-splitting. However, problems with this approach are evident once it comes to adding the reducer to a Redux store. // data/createStore.js import { combineReducers, createStore } from 'redux'; Import notifications from './notifications'; const initialState = /* from local storage or server */ const reducer = combineReducers({ notifications }); const store = createStore(reducer, initialState); export default store; You’ll notice that the notifications namespace is defined at the time the store is created, and not by the Redux module that defines the reducer. If the “notifications” reducer name is changed in createStore, all the selectors in the “notifications” Redux module no longer work. Worse, every Redux module needs to be imported in the createStore file before it can be added to the store’s reducer. This doesn’t scale and isn’t good for large apps that rely on code-splitting to incrementally load modules. A large app could have dozens of Redux modules, many of which are only used by a few components and unnecessary for initial render. Both of these issues can be avoided by introducing a Redux reducer registry. Redux reducer registry The reducer registry enables Redux reducers to be added to the store’s reducer after the store has been created. This allows Redux modules to be loaded on-demand, without requiring all Redux modules to be bundled in the main chunk for the store to correctly initialize. // data/reducerRegistry.js export class ReducerRegistry { constructor() { this._emitChange = null; this._reducers = {}; } getReducers() { return { ...this._reducers }; } register(name, reducer) { this._reducers = { ...this._reducers, [name]: reducer }; if (this._emitChange) { this._emitChange(this.getReducers()); } } setChangeListener(listener) { this._emitChange = listener; } } const reducerRegistry = new ReducerRegistry(); export default reducerRegistry; Each Redux module can now register itself and define its own reducer name. // data/notifications/index.js import reducerRegistry from '../reducerRegistry'; const initialState = []; let notificationId = 0; const reducerName = 'notifications'; const createActionName = name => `app/${reducerName}/${name}`; // reducer export default function reducer(state = initialState, action = {}) { switch (action.type) { case ADD_NOTIFICATION: return [...state, { ...action.payload, id: notificationId += 1 }]; case REMOVE_NOTIFICATION: return state.slice(1); default: return state; } } reducerRegistry.register(reducerName, reducer); // selectors export const selectAllNotifications = state => state[reducerName]; export const selectNextNotification = state => state[reducerName][0]; // actions export const ADD_NOTIFICATION = createActionName(ADD_NOTIFICATION); export const REMOVE_NOTIFICATION = createActionName(REMOVE_NOTIFICATION); // action creators export const addNotification = payload => ({ payload, type: ADD_NOTIFICATION }); export const removeNotification = () => ({ type: REMOVE_NOTIFICATION }); Next, we need to replace the store’s combined reducer whenever a new reducer is registered (e.g., after loading an on-demand chunk). This is complicated slightly by the need to preserve initial state that may have been created by reducers that aren’t yet loaded on the client. By default, once an action is dispatched, Redux will throw away state that is not tied to a known reducer. To avoid that, reducer stubs are created to preserve the state. // data/createStore.js import { combineReducers, createStore } from 'redux'; import reducerRegistry from './reducerRegistry'; const initialState = /* from local storage or server */ // Preserve initial state for not-yet-loaded reducers const combine = (reducers) => { const reducerNames = Object.keys(reducers); Object.keys(initialState).forEach(item => { if (reducerNames.indexOf(item) === -1) { reducers[item] = (state = null) => state; } }); return combineReducers(reducers); }; const reducer = combine(reducerRegistry.getReducers()); const store = createStore(reducer, initialState); // Replace the store's reducer whenever a new reducer is registered. reducerRegistry.setChangeListener(reducers => { store.replaceReducer(combine(reducers)); }); export default store; Managing the Redux store’s reducer with a registry should help you better code-split your application and modularize your state management. Full Article
science and technology Making SVG icon libraries for React apps By nicolasgallagher.com Published On :: Thu, 01 Feb 2018 16:00:00 -0800 Using SVG is currently the best way to create icon libraries for apps. Icons built with SVG are scalable and adjustable, but also discrete, which allows them to be incrementally loaded and updated. In contrast, icons built as fonts cannot be incrementally loaded or updated. This alone makes SVG icons the better choice for high-performance apps that rely on code-splitting and incremental deploys. This post describes how to make a package of React components from a library of SVG icons. Although I’m focusing on React, making any other type of package is also possible. At Twitter I used the approach described here to publish the company’s SVG icon library in several different formats: optimized SVGs, plain JavaScript modules, React DOM components, and React Native components. Using the icons The end result is a JavaScript package that can be installed and used like any other JavaScript package. yarnpkg add @acme/react-icons Each icon is available as an individually exported React component. import IconCamera from '@acme/react-icons/camera'; This allows your module bundler to package only the icons that are needed, and icons can be efficiently split across chunks when using code-splitting. This is a significant advantage over icon libraries that require fonts and bundle all icons into a single component. // entire icon library is bundled with your app import Icon from '@acme/react-icons'; const IconCamera = <Icon name='camera' />; Each icon is straightforward to customize (e.g., color and dimensions) on a per-use basis. import IconCamera from '@twitter/react-icons/camera'; const Icon = ( <IconCamera style={{ color: 'white', height: '2em' }} /> ); Although the icons render to SVG, this is an implementation detail that isn’t exposed to users of the components. Creating components Each React component renders an inline SVG, using path and dimensions data extracted from the SVG source files. A helper function called createIconComponent means that only a few lines of boilerplate are needed to create a component from SVG data. import createIconComponent from './utils/createIconComponent'; import React from 'react'; const IconCamera = createIconComponent({ content: <g><path d='...'></g>, height: 24, width: 24 }); IconCamera.displayName = 'IconCamera'; export default IconCamera; This is an example of what the createIconComponent function looks like when building components for a web app like Twitter Lite, which is built with React Native for Web. // createIconComponent.js import { createElement, StyleSheet } from 'react-native-web'; import React from 'react'; const createIconComponent = ({ content, height, width }) => (initialProps) => { const props = { ...initialProps, style: StyleSheet.compose(styles.root, initialProps.style), viewBox: `0 0 ${width} ${height}` }; return createElement('svg', props, content); }; const styles = StyleSheet.create({ root: { display: 'inline-block', fill: 'currentcolor', height: '1.25em', maxWidth: '100%', position: 'relative', userSelect: 'none', textAlignVertical: 'text-bottom' } }); Setting the fill style to currentcolor allows you to control the color of the SVG using the color style property instead. All that’s left is to use scripts to process the SVGs and generate each React component. Creating icon packages A complete example of one way to do this can be found in the icon-builder-example repository on GitHub. The project structure of the example tool looks like this. . ├── README.md ├── package.json ├── scripts/ ├── build.js ├── createReactPackage.js └── svgOptimize.js └── src/ ├── alerts.svg ├── camera.svg ├── circle.svg └── ... The build script uses SVGO to optimize the SVGs, extract SVG path data, and extract metadata. The example packager for React then uses templates to create a package.json and the React icon components shown earlier. import createIconComponent from './utils/createIconComponent'; import React from 'react'; const ${componentName} = createIconComponent({ height: ${height}, width: ${width}, content: <g>${paths}</g> }); ${componentName}.displayName = '${componentName}'; export default ${componentName}; Additional packagers can be included to build other package types from the same SVG source. When the underlying icon library changes, it only takes a couple of commands to rebuild hundreds of icons and publish new versions of each package. Full Article
science and technology JAMA Oncology : Cost-effectiveness and Benefit-to-Harm Ratio of Risk-Stratified Breast Cancer Screening By traffic.libsyn.com Published On :: Thu, 05 Jul 2018 15:00:00 +0000 Interview with Nora Pashayan, BSc MSc MSt MD PhD FFPH, author of Cost-effectiveness and Benefit-to-Harm Ratio of Risk-Stratified Screening for Breast Cancer: A Life-Table Model Full Article
science and technology JAMA Pediatrics : Effects of Early Solid Food Introduction on Infant Sleep By traffic.libsyn.com Published On :: Mon, 09 Jul 2018 15:16:35 +0000 This summary reviews a randomized clinical trial that explores the effect of the early introduction of solid foods on infant sleep. Association of Early Introduction of Solids With Infant Sleep: A Secondary Analysis of a Randomized Clinical Trial Full Article
science and technology JAMA Psychiatry : Association of Safety Planning Intervention With Subsequent Suicidal Behavior Among ER-Treated Suicidal Patients By traffic.libsyn.com Published On :: Wed, 11 Jul 2018 15:00:00 +0000 Interview with Barbara H. Stanley, author of Comparison of the Safety Planning Intervention With Follow-up vs Usual Care of Suicidal Patients Treated in the Emergency Department Full Article
science and technology JAMA Internal Medicine : Association Between Offering Limited LVEF Echocardiograms and Overall Use of Echocardiography By traffic.libsyn.com Published On :: Mon, 23 Jul 2018 15:00:00 +0000 Interview with Alexander T. Sandhu, MD, MS, author of Association Between Offering Limited Left Ventricular Ejection Fraction Echocardiograms and Overall Use of Echocardiography Full Article
science and technology JAMA Cardiology : Efficacy and Safety of Further Lowering of LDL Cholesterol in Patients Starting With Very Low Levels By traffic.libsyn.com Published On :: Wed, 01 Aug 2018 15:00:00 +0000 Interview with Marc S. Sabatine, MD, MPH, author of Efficacy and Safety of Further Lowering of Low-Density Lipoprotein Cholesterol in Patients Starting With Very Low Levels: A Meta-analysis Full Article
science and technology JAMA Facial Plastic Surgery : Selfies—Living in the Era of Filtered Photographs By traffic.libsyn.com Published On :: Thu, 02 Aug 2018 15:00:00 +0000 Interview with Neelam A. Vashi, MD, author of Selfies—Living in the Era of Filtered Photographs Full Article
science and technology JAMA Oncology : Maintenance Treatment and Survival in Patients With Myeloma By traffic.libsyn.com Published On :: Thu, 09 Aug 2018 15:00:00 +0000 Interview with Francesca Gay, author of Maintenance Treatment and Survival in Patients With Myeloma: A Systematic Review and Network Meta-analysis Full Article
science and technology JAMA Internal Medicine : Evaluating Shared Decision Making for Lung Cancer Screening By traffic.libsyn.com Published On :: Mon, 13 Aug 2018 15:00:00 +0000 Interview with Daniel Scott. Reuland, MD MPH, author of Evaluating Shared Decision Making for Lung Cancer Screening, and Bruno Heleno, MD, PhD, author of Estimation of Overdiagnosis of Lung Cancer in Low-Dose Computed Tomography Screening Full Article
science and technology JAMA Surgery : Association of Compensation to Physicians From Industry and Self-declared Conflict of Interest By traffic.libsyn.com Published On :: Wed, 15 Aug 2018 15:00:00 +0000 Interview with Mehraneh D. Jafari, MD, and Kasra Ziai, MD, authors of Association of Compensation From the Surgical and Medical Device Industry to Physicians and Self-declared Conflict of Interest Full Article
science and technology JAMA Cardiology : Association of Left Ventricular Global Longitudinal Strain With Asymptomatic Severe Aortic Stenosis By traffic.libsyn.com Published On :: Wed, 15 Aug 2018 15:00:00 +0000 Interview with Jeroen J. Bax, MD, PhD, author of Association of Left Ventricular Global Longitudinal Strain With Asymptomatic Severe Aortic Stenosis: Natural Course and Prognostic Value, and James D. Thomas, author of Moving Past Ejection Fraction in Timing of Aortic Stenosis Intervention Full Article
science and technology JAMA Otolaryngology–Head & Neck Surgery : Effect of a Change in Papillary Thyroid Cancer Terminology on Anxiety Levels and Treatment Preferences By traffic.libsyn.com Published On :: Thu, 23 Aug 2018 15:00:00 +0000 Interview with Brooke Nickel and Juan Brito, MD, MSc, authors of Effect of a Change in Papillary Thyroid Cancer Terminology on Anxiety Levels and Treatment Preferences: A Randomized Crossover Trial Full Article
science and technology JAMA Ophthalmology : Association of Preclinical Alzheimer Disease With OCT Angiographic Findings By traffic.libsyn.com Published On :: Thu, 23 Aug 2018 15:00:00 +0000 Interview with Rajendra S. Apte, MD PhD, and Gregory P Van Stavern, MD, authors of Association of Preclinical Alzheimer Disease With Optical Coherence Tomographic Angiography Findings Full Article
science and technology JAMA Neurology : Effect of Dextroamphetamine on Poststroke Motor Recovery By traffic.libsyn.com Published On :: Mon, 27 Aug 2018 15:00:00 +0000 Interview with Larry B. Goldstein, MD, author of Effect of Dextroamphetamine on Poststroke Motor Recovery: A Randomized Clinical Trial Full Article
science and technology JAMA Internal Medicine : Effect of Group-Administered Behavioral Treatment on Urinary Incontinence in Older Women By traffic.libsyn.com Published On :: Tue, 04 Sep 2018 15:00:00 +0000 Interview with Ananias C Diokno, MD, author of Effect of Group-Administered Behavioral Treatment on Urinary Incontinence in Older Women: A Randomized Clinical Trial Full Article
science and technology JAMA Otolaryngology–Head & Neck Surgery : Symptom Burden Associated With Late Lower Cranial Neuropathy in Long-term Oropharyngeal Cancer Survivors By traffic.libsyn.com Published On :: Thu, 06 Sep 2018 15:00:00 +0000 Interview with Katherine A. Hutcheson, PhD, author of Symptom Burden Associated With Late Lower Cranial Neuropathy in Long-term Oropharyngeal Cancer Survivors Full Article
science and technology JAMA Oncology : Hyperprogressive Disease in Patients With Advanced NSCLC Treated With PD-1/PD-L1 Inhibitors By traffic.libsyn.com Published On :: Thu, 06 Sep 2018 15:00:00 +0000 Interview with Benjamin Besse, MD, author of Hyperprogressive Disease in Patients With Advanced Non–Small Cell Lung Cancer Treated With PD-1/PD-L1 Inhibitors or With Single-Agent Chemotherapy Full Article
science and technology JAMA Cardiology : Aortic Vascular Inflammation and Coronary Artery Disease in Psoriasis By traffic.libsyn.com Published On :: Wed, 12 Sep 2018 15:00:00 +0000 Interview with Nehal N. Mehta, MD, MSCE, author of Association Between Aortic Vascular Inflammation and Coronary Artery Plaque Characteristics in Psoriasis, and Jagat Narula, MD, PhD, author of Inflammation, Superadded Inflammation, and Out-of-Proportion Inflammation in Atherosclerosis Full Article
science and technology JAMA Psychiatry : Association of Cannabinoid Administration With Experimental Pain in Healthy Adults By traffic.libsyn.com Published On :: Wed, 19 Sep 2018 15:00:00 +0000 Interview with Emily B. Ansell, PhD, and Martin J. De Vita, MS, authors of Association of Cannabinoid Administration With Experimental Pain in Healthy Adults: A Systematic Review and Meta-analysis Full Article
science and technology JAMA Dermatology : Differences in Use of Outpatient Dermatology Services in the United States By traffic.libsyn.com Published On :: Wed, 26 Sep 2018 15:00:00 +0000 Interview with Raghav Tripathi, MPH, and Jeremy Scott Bordeaux, MD, MPH, authors of Association of Demographic and Socioeconomic Characteristics With Differences in Use of Outpatient Dermatology Services in the United States Full Article
science and technology JAMA Facial Plastic Surgery : Superiorly Pediculated Superficial Musculoaponeurotic System Ligament Flap to Control the Supratip By traffic.libsyn.com Published On :: Thu, 27 Sep 2018 15:00:00 +0000 Interview with Roxana Cobo, MD, author of Superiorly Pediculated Superficial Musculoaponeurotic System Ligament Flap to Control the Supratip Full Article
science and technology JAMA Ophthalmology : Attaining Independent Funding Among NIH K Grant Awardees in Ophthalmology By traffic.libsyn.com Published On :: Thu, 27 Sep 2018 15:00:00 +0000 Interview with Daniel L. Chao, MD, PhD, author of Success in Attaining Independent Funding Among National Institutes of Health K Grant Awardees in Ophthalmology: An Extended Follow-up Full Article
science and technology JAMA Oncology : Analgesic Use and Risk of Ovarian Cancer in the Nurses’ Health Studies By traffic.libsyn.com Published On :: Thu, 04 Oct 2018 15:00:00 +0000 Interview with Mollie E. Barnard, ScD, and Shelley Tworoger, PhD, authors of Association of Analgesic Use With Risk of Ovarian Cancer in the Nurses’ Health Studies, and Andrew T. Chan, MD, MPH, author of Association Between Aspirin Use and Risk of Hepatocellular Carcinoma Full Article
science and technology JAMA Dermatology : Risk of Melanoma in Moderately Dysplastic Nevi Excisionally Biopsied but With Positive Margins By traffic.libsyn.com Published On :: Wed, 10 Oct 2018 15:00:00 +0000 Interview with Caroline C. Kim, MD, author of Risk of Subsequent Cutaneous Melanoma in Moderately Dysplastic Nevi Excisionally Biopsied but With Positive Histologic Margins Full Article
science and technology JAMA Ophthalmology : Two-Year Efficacy of Ranibizumab Plus Laser-Induced Chorioretinal Anastomosis vs Ranibizumab for CRVO By traffic.libsyn.com Published On :: Thu, 18 Oct 2018 15:00:00 +0000 Interview with Ian Leslie. Mcallister, FRANZCO, author of Two-Year Efficacy of Ranibizumab Plus Laser-Induced Chorioretinal Anastomosis vs Ranibizumab Monotherapy for Central Retinal Vein Occlusion: A Randomized Clinical Trial Full Article
science and technology JAMA Internal Medicine : Association of Organic Food Consumption With Cancer Risk By traffic.libsyn.com Published On :: Mon, 22 Oct 2018 15:00:00 +0000 Interview with Julia Baudry, PhD, author of Association of Frequency of Organic Food Consumption With Cancer Risk: Findings From the NutriNet-Santé Prospective Cohort Study, and Jorge E. Chavarro, MD, ScD, author of Organic Foods for Cancer Prevention—Worth the Investment? Full Article
science and technology JAMA Neurology : Association of Apolipoprotein E ε4 With Transactive Response DNA-Binding Protein 43 By traffic.libsyn.com Published On :: Mon, 22 Oct 2018 15:00:00 +0000 Interview with Keith A. Josephs, MD, MST, MSc, author of Association of Apolipoprotein E ε4 With Transactive Response DNA-Binding Protein 43 Full Article
science and technology JAMA Surgery : Evaluation of Wound Photography for Remote Postoperative Assessment By traffic.libsyn.com Published On :: Wed, 24 Oct 2018 15:00:00 +0000 Interview with Benjamin K. Poulose, MD MPH, author of Evaluation of Wound Photography for Remote Postoperative Assessment of Surgical Site Infections Full Article
science and technology JAMA Ophthalmology : Donor, Recipient, and Operative Factors Associated With Increased Endothelial Cell Loss in the Cornea Preservation Time Study By traffic.libsyn.com Published On :: Fri, 26 Oct 2018 13:30:00 +0000 Interview with Jonathan H. Lass, MD, author of Donor, Recipient, and Operative Factors Associated With Increased Endothelial Cell Loss in the Cornea Preservation Time Study Full Article
science and technology JAMA Pediatrics : Exposure to High-Performing Schools and Reduced Adolescent Substance Use By traffic.libsyn.com Published On :: Mon, 29 Oct 2018 15:00:00 +0000 Interview with Rebecca N. Dudovitz, MD, MSHS, author of Assessment of Exposure to High-Performing Schools and Risk of Adolescent Substance Use: A Natural Experiment Full Article
science and technology JAMA Psychiatry : Association of Autism Spectrum Disorder with Prenatal Exposure to Medication Affecting Neurotransmitter Systems By traffic.libsyn.com Published On :: Wed, 31 Oct 2018 15:00:00 +0000 Interview with Magdalena Janecka, Ph.D., author of Association of Autism Spectrum Disorder With Prenatal Exposure to Medication Affecting Neurotransmitter Systems Full Article
science and technology JAMA Otolaryngology–Head & Neck Surgery : Trends in Health Care Costs and Utilization Associated With Untreated Hearing Loss Over 10 Years By traffic.libsyn.com Published On :: Thu, 08 Nov 2018 16:00:00 +0000 Interview with Nicholas S. Reed, Au.D, author of Trends in Health Care Costs and Utilization Associated With Untreated Hearing Loss Over 10 Years Full Article
science and technology JAMA Pediatrics : Association of Nurse Workload With Missed Nursing Care in the NICU By traffic.libsyn.com Published On :: Mon, 12 Nov 2018 16:00:00 +0000 Interview with Heather L. Tubbs-Cooley, RN, PhD, author of Association of Nurse Workload With Missed Nursing Care in the Neonatal Intensive Care Unit Full Article
science and technology JAMA Internal Medicine : Effect of Exercise Intervention on Functional Decline in Very Elderly Patients By traffic.libsyn.com Published On :: Mon, 12 Nov 2018 16:00:00 +0000 Interview with Mikel Izquierdo, Ph.D, and Nicolás Martínez-Velilla, PhD, authors of Effect of Exercise Intervention on Functional Decline in Very Elderly Patients During Acute Hospitalization: A Randomized Clinical Trial, and William J. Hall, MD, author of A Novel Exercise Intervention and Functional Status in Very Elderly Patients During Acute Hospitalization Full Article
science and technology JAMA Psychiatry : Testosterone Treatment and Alleviation of Depression in Men By traffic.libsyn.com Published On :: Wed, 14 Nov 2018 16:00:00 +0000 Interview with Andreas Walther, PhD, author of Association of Testosterone Treatment With Alleviation of Depressive Symptoms in Men: A Systematic Review and Meta-analysis Full Article
science and technology JAMA Ophthalmology : Factors in Premature Infants Associated With Low Risk of Developing Retinopathy of Prematurity By traffic.libsyn.com Published On :: Thu, 15 Nov 2018 16:00:00 +0000 Interview with Kelly Wade, MD, PhD, MSCE, and Graham Quinn, MD, MSCE, authors of Factors in Premature Infants Associated With Low Risk of Developing Retinopathy of Prematurity Full Article
science and technology JAMA Oncology : Surgery vs Radiotherapy in the Management of Gleason Score 9-10 Prostate Cancer and the Risk of Mortality By traffic.libsyn.com Published On :: Thu, 15 Nov 2018 16:00:00 +0000 Interview with Anthony D'Amico, MD, PhD, author of Surgery vs Radiotherapy in the Management of Biopsy Gleason Score 9-10 Prostate Cancer and the Risk of Mortality Full Article
science and technology JAMA Dermatology : Economic Evaluation of Talimogene Laherparepvec Plus Ipilimumab vs Ipilimumab Alone for Unresectable Melanoma By traffic.libsyn.com Published On :: Wed, 21 Nov 2018 16:00:00 +0000 Interview with Ivo Abraham, PhD, author of Economic Evaluation of Talimogene Laherparepvec Plus Ipilimumab Combination Therapy vs Ipilimumab Monotherapy in Patients With Advanced Unresectable Melanoma Full Article
science and technology JAMA Cardiology : Association of Quality of Life Outcomes With Transcatheter Mitral Valve Repair By traffic.libsyn.com Published On :: Wed, 21 Nov 2018 16:00:00 +0000 Interview with Suzanne V. Arnold, MD, MHA, author of Association of Transcatheter Mitral Valve Repair With Quality of Life Outcomes at 30 Days and 1 Year: Analysis of the Transcatheter Valve Therapy Registry Full Article
science and technology JAMA Surgery : Effectiveness of Prophylactic Intraperitoneal Mesh Implantation for Prevention of Incisional Hernia By traffic.libsyn.com Published On :: Wed, 21 Nov 2018 16:00:00 +0000 Interview with Guido Beldi, Consultant Surgeon, author of Effectiveness of Prophylactic Intraperitoneal Mesh Implantation for Prevention of Incisional Hernia in Patients Undergoing Open Abdominal Surgery: A Randomized Clinical Trial Full Article
science and technology JAMA Cardiology : Association of Race/Ethnicity With Oral Anticoagulant Use in Patients With Atrial Fibrillation By traffic.libsyn.com Published On :: Wed, 28 Nov 2018 16:00:00 +0000 Interview with Utibe R Essien, MD, and Daniel E. Singer, MD, authors of Association of Race/Ethnicity With Oral Anticoagulant Use in Patients With Atrial Fibrillation: Findings From the Outcomes Registry for Better Informed Treatment of Atrial Fibrillation II Full Article
science and technology JAMA Facial Plastic Surgery : Evaluation of Antibiotic Prophylaxis in Rhinoplasty By traffic.libsyn.com Published On :: Thu, 29 Nov 2018 16:00:00 +0000 Interview with Sam P. Most, MD, author of Evaluation of Antibiotic Prophylaxis in Rhinoplasty: A Systematic Review and Meta-analysis Full Article
science and technology JAMA Oncology : Association of Body Fat and Risk of Breast Cancer in Postmenopausal Women With Normal BMI By traffic.libsyn.com Published On :: Thu, 06 Dec 2018 16:00:00 +0000 Interview with Andrew J. Dannenberg, MD, author of Association of Body Fat and Risk of Breast Cancer in Postmenopausal Women With Normal Body Mass Index: A Secondary Analysis of a Randomized Clinical Trial and Observational Study Full Article