2nd December, 2018
I remember a time when React was new and people struggled with the decision of what folder structure you should use to layout your project. Do I put everything in the Components
folder? Do we use Screens
or Pages
for main page components and then put reusable ones inside Components
? Do I have 50 index.js
files inside of all my folders?
Due to React being a rendering library and not a framework this type of choice was left up to the user, which is both empowering and also for some confusing.
I think today most people know how they like to structure things, and for devs who are new we have docs, Create React App which gives you a starting point or something like Gatsby which gives you a much stricter guideline.
Recently I was given the challenge of building a proof of concept (PoC) for a new product. We would be looking to build it in React and so the PoC had to not only demonstrate the advantages of building an SPA using React, but also that it could handle things like:
Having built several React apps before I was not worried about showing off what makes React great. It would however be the first time I have had to create a project for the purpose of white labelling.
I started searching for articles or examples out there that might give me a hint into how other people were handling this problem. Alas I couldn’t really find any - they probably exist but I didn’t find them.
Side Note: Whilst every example for React Context references themeing - it is talking about runtime themes. I am not looking for runtime themes at the moment - build time themes are my need right now.
I turned to the our mobile apps, we use a hybrid mobile framework that supports themes (build time themes) out of the box as well as environment variables.
I set about thinking how I could get webpack to provide me with the same build time configuration.
I knew about webpack resolve.alias and so I set about figuring out how to leverage this for my solution.
The aim of this article is not to preach a solution for all projects, but to provide a solution so that people who are looking for a way to pull this off - and are lost - have something to follow.
I started by adding 2 new folders to my src
folder:
Anything that relates to a particular theme but is environment specific we place in the settings theme/environment file.
An example of some things that I am putting in here is:
settings/base/development.js
export default {
versionName: 'AAA',
versionNumber: '0.0.1',
baseUrl: 'http://localhost:3000/',
apiUrl: 'http://localhost:8000/',
}
settings/theme1/development.js
import base from '../base/development'
export default Object.assign({}, base, {
apiVersion: `DEV-Theme1-ProjectName-${base.versionNumber}`,
version: `DEV-Theme1-${base.versionName}-${base.versionNumber}`,
})
You could imagine using this to specify a different API URL for local dev / test / staging / production, among other settings. You might also have API keys or other settings you want to store here.
An example of things in the theme files:
themes/base.js
const darkGray = '#9B9B9B'
const midGray = '#D7D7D7'
const lightGray = '#EEEFEF'
export default {
colors: {
darkGray,
midGray,
lightGray,
}
}
themes/theme1.js
import base from './base'
const primary = '#3498DB'
const secondary = '#F26723'
export default Object.assign({}, base, {
mainLogo: 'logo.png',
colors: Object.assign({}, base.colors, {
primary,
secondary,
}),
})
This should provide the flexibility to have base settings that can be overridden per theme and also per environment.
My initial thought was to simply set this in my package.json file:
{
...
"themeName": "theme1"
...
}
whilst this does work well for local development, I ran into problems trying to use this for automated deployments.
I decided that the best approach was to use this for local dev - and then use actual process.env.THEME_NAME
when building for production.
Create aliases to import or require certain modules more easily
module.exports = {
//...
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/')
}
}
};
So webpack can create aliases for us to make importing easier
🤔; how about then instead of simply making importing files easier, we actually just point to the correct file based on what theme we might want to use and what NODE_ENV
we are in.
NB: production config
const themePath = path.resolve(__dirname, `../src/themes/${process.env.THEME_NAME}.js`);
const settingsBase = path.resolve(__dirname, `../src/settings/${process.env.THEME_NAME}`);
let settingsPath = '';
if (process.env.NODE_ENV === 'development') {
settingsPath = `${settingsBase}/development.js`;
} else if (process.env.NODE_ENV === 'staging') {
settingsPath = `${settingsBase}/staging.js`;
} else {
settingsPath = `${settingsBase}/master.js`;
}
module.exports = {
//...
resolve: {
alias: {
theme: themePath,
settings: settingsPath,
}
}
};
Now throughout the app we can simply:
import theme from 'theme'
console.log(theme.mainLogo) // logo.png
12th August, 2019
Recently at my workplace there was a need to create an external running service responsible for running integration tests against all the…
5th February, 2019
Your stomach drops, you feel the sweat start to bead on your forehead, you start saying “no” a lot probably in quick succession. You have…
5th November, 2018
If you have been reading Javascript lately you might have come across the three dots (ellipsis) in one form or another. If you saw this in a…