Make sure you have yarn
installed globally and your version of node
is at least 6.9.1
and you npm
version at least 4.0.2
.
- Run make install
- Run make start. A list of available servers will appear in your terminal. Visit one of those to see the app.
- Update your Homestead yaml file.
- Update your hosts.
- Provision you homestead.
- Run
make install
and visit the url you've setup in your yaml file.
Run make webpack-watch
The assets
folder will contain the following:
- css
- fonts
- img
- react
The css
within the assets
folder will contain global css. Note that React discourages the usage of global css but I found useful to have a global configuration for typography & fonts
, normalize
and breakpoints
.
The Boilerplate uses React CSS Modules
all of the CSS you will write in a specific component will apply to that compoennt only. The global css won't be able to override or interfer with the component's css because we also make use of classnames
.
classnames
will append a prefix to each class name React uses. This way the global css won't override or interfer with the component's css.
The fonts
folder will contain all of the fonts used across the website and they will be compiled from webpack and then modularized in our bundle.js
.
The images
folder will contain both, images and SVGs. Webpack will take care of the compilation.
Note an SVG sprite won't be generated, please refer to the SVG
section below to know more about it.
Import the helper in the file you wish to display the SVG:
import { getImage } from '../../helpers/general';
Use your image in the React Component:
<img src={getImage('./root/logo.png')} />
Webpack will modularize the SVGs for you in the bundle.js
. I wrote an helper that you can find at ./assets/react/helpers/general.js
which will get the resolved path to a specific image. In order to use an image you can use the syntax below.
Import the helper in the file you wish to display the SVG:
import { getImage } from '../../helpers/general';
Import react-svg-inline
from the node modules:
import SVGInline from 'react-svg-inline';
Use your image in the React Component:
<SVGInline svg={getImage('./svg/logo.svg')} />
In the React folder there is a lot (a lot) going on. You will find the following:
- components
- constants
- helpers
- redux
- strings
- app.js
In here you can write your components plus App
and Main
folders. In App
all of the Redux actions will be created and dispatched to the child component (Main).
Main
includes the Navigation
and the Footer
components (I feel like these 2 components should stay rather than import them everytime in each component, essentially this way prevent code dusplications).
I will update the boilerplate, ideally I'd like to create a sort of component generator, Yeoman
seems nice but I haven't had time to crack on with it.
For the time being please follow this structure:
ComponentName --
|
|--ComponentNameContainer.js
|--ComponentName.js
|--ComponentName.scss
The ComponentNameContainer
will contain the logic of your component (if applicable), for instance the container you would make API calls, updating props.
The ComponentName.js
will be the view of your component and won't contain strong logic; it'll receive props, data from API response (All of the data already manipulated from its container).
Constants will contain mainly exportable/importable variables.
Helpers will contain exportable/importable methods and configurations, as in:
- API Configuration
- API methods
- Auth methods
- Mapped API responses
- Generic helpers (String manipulations, Analytics)
The Redux folder is pretty complicated. Redux is a state container for JS Application. Why we use it? Because it's very helpful when you want 2 separate components to communicate; think at Redux like a huge container of React states, usable from any component at any level.
3 folders and 1 file here:
- actions
- data
- reducers
- store.js
The action
is the method you call to update a React state.
The data
the React state that the action
will change.
The reducer
calculates the next state based on the previous state and the dispatched action.
Example of action
:
export function toggleNavigationState(status) {
return {
status,
type: 'TOGGLE_NAVIGATION',
};
}
Here status
is the value of the next state. Let's say status is false
by default. If you this.props.toggleNavigationStatus(true)
, true
will be the new state.
Type
is the action type being used from the reducer.
Example of data
:
export default {
status: false,
};
Here status
is the default state, when the application start, the React status will be false
.
Example of reducer
:
import update from 'immutability-helper';
export default function toggleNavigationReducer(state = {}, action) {
switch (action.type) {
case 'TOGGLE_NAVIGATION':
return update(state, {
status: { $set: action.status },
});
default:
return state;
}
}
As you can see, this switch
returns an object based on the action type (TOGGLE_NAVIGATION).
The string system will allow you to write some copy (used in the website) in JSON files. There is an helper function that will take care of abstracting the strings from the JSON files for you. You can reference the string in your component so there won't be any hardcoded
strings.
Example:
This is the string you want to pull thru the fron-end.
{
"string": "translated string"
}
Head to ./assets/react/helpers/strings.js
and import the json file at the top like this:
import general from '../strings/general.json';
Set the string, in the same file, right at the bottom like this:
setString('general', general);
Use the string somewhere in your component using the helper function (rembember to import the helper function in your React component)
import { getString } from '../helpers/strings';
{getString('general.string')}
The above code will output some text:
translated string
You can translate your App making use of react-intl
. All you need to do is:
- Add a new locale in
assets/react/i18n.js
- Add a new JSON file in
assets/react/translations
- Create a
messages/index.js
within the component you wish to translate and reference the strings you want to translate like so:
import { defineMessages } from 'react-intl';
export default defineMessages({
page: {
id: 'boilerplate.components.Navigation.item.page',
defaultMessage: 'Page',
},
});
- Translate the strings in the component:
import { FormattedMessage } from 'react-intl';
import messages from './messages';
<FormattedMessage {...messages.page} />
Note: If you do not need this plugin you can remove the <IntlProvider>
component from the React Router Tree in assets/react/app.js
Routing our App is fairly easy. All the magic happens in ./assets/react/app.js
.
render((
<Provider store={store}>
<Router history={history} onUpdate={() => window.scrollTo(0, 0)}>
<Route path="/" component={App}>
<IndexRoute component={LandingPageContainer} />
</Route>
</Router>
</Provider>
), document.getElementById('root'));
The Provider will host the Redux Store.
<Route path="/" component={ComponentNameContainer} />
All of your new routes will be placed within the main Route (App
).
In order to keep our project tidy and well readable I'd use:
- .editorcofig (it defines some basic rules for sass, php, js, json etc etc)
- .eslintrc (it uses the AirBnb rules, it throws errors when writing ES6 code in your sublime file and when compiling with webpack)
- .stylelintrc (it uses the AirBnb rules, it throws errors when writing sass in your sublime file and when compiling with webpack)
Please create a PR towards master
if you wish to contribute and improving the boilerplate.
More to come ...