How To Build Your Website With NextJS
Tutorial on the basics of NextJS for building a website.
Javascript ecosystem is really rich: full of developers but also full of frameworks and libraries.
When you want to develop a frontend application, whatever its rendering framework, you will have to structure things into your project in order to organize the data management with views. This case occurs particularly when you use component rendering frameworks like React
or VueJS
.
Historically, this has been needed by React so that's why Facebook has open sourced its tool named Flux.
Here is the philosophy:
Your application declare actions
for each components. These actions allow you to define the state of your data which is stored in a store
. This stores continually maintains your view
up-to-date.
We have a drawback in this case because you have to define one store per component. This is working but on large applications you can feel limited with it.
In June 2015, Dan Abramov has launched Redux which simplify store management because you only have one store for all your application.
All of your application components can access to the whole state.
For more information about Redux/Flux differences I encourage you to have a look at Dan's answer on this subject.
This article will deal about how to install and use Redux on your own projects.
Please keep in mind that Redux can be used with multiple rendering frameworks like React or VueJS.
To install Redux, you will just need the redux
npm (or yarn) package.
If you use Redux into a React application, you will also need the react-redux
package or even the vue-redux
if you want to use it on a VueJS project.
$ yarn add redux
Nothing more, you can now start to use Redux.
As previously described, you will have to instanciate a new store
that will allow to store the state of all your application.
In order to instanciate this store, you will have to give to it some reducers
. Reducers contain methods that change the state of your application.
These state changes occur when an action
is dispatched by your application.
Here we are, we have the 3 things needed by a Redux application: actions
, reducers
and a store
.
We will use a simple practical case: a counter that we can increment or decrement with a given value.
Here is our target arborescence:
src/
├── actions
│ └── counter.js
├── constants
│ └── ActionTypes.js
├── reducers
│ ├── another.js
│ ├── counter.js
│ └── index.js
└── store
└── configureStore.js
Let's write an actions containing file that will implement our 2 actions: increment and decrement. Before all, we will store these actions names into constants in order to keep our code clear and comprehensible as we will always call these constants in all of our code.
Start by creating a src/constants/ActionTypes.js
file with the following content:
export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT';
Great, we will now write actions that correspond to these constants in a src/actions/counter.js
file:
import * as types from '../constants/ActionTypes';
export const increment = (value) => ({ type: types.INCREMENT, value });
export const decrement = (value) => ({ type: types.DECREMENT, value });
You have just created your 2 actions (increment
and decrement
) which each have a type property (required) and a value to add or remove to the current counter value.
We will now write reducers functions that correspond to the actions we previously wrote in order to update the value in our application state.
This will be written in the src/reducers/counter.js
file:
import { INCREMENT, DECREMENT } from '../constants/ActionTypes';
const initialState = {
current: 0,
};
export default function counter(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return {
current: state.current += action.value,
};
case DECREMENT:
return {
current: state.current -= action.value,
};
default:
return state;
}
}
You got the idea, we have our actions wrapped into a switch() { case ... }
and directly return the store updated with new values.
You can also observe that we have initialized an initial state (initialState) in order to prepare our application state with some default values.
Note:
You can write as many reducers as you need in your application so you can clearly split your code application.
Only point if you declare multiple reducers into your application is that you will have to combine them here in a file named src/reducers/index.js
as follows:
import { combineReducers } from 'redux'; import counter from './counter'; import another from './another'; const reducers = combineReducers({ counter, another, }); export default reducers;
You have your actions and your reducers so let's dive into the final step: store creation!
Store will be created in a src/store/configureStore.js
file with only these couple of lines:
import { createStore } from 'redux';
import reducers from '../reducers';
const configureStore = () => {
return createStore(
reducers,
);
};
export default configureStore;
You just have to call the Redux's createStore()
API function in order to create your store.
In order to go further, please note that this function can take a maximum of 3 arguments:
A middleware is a callback that is executed each time Redux can the dispatch()
function so each time an action is triggered.
Here is a simple middleware that logs each dispatched actions:
import { createStore, applyMiddleware } from 'redux'
import reducers from '../reducers';
function logger({ getState }) {
return (next) => (action) => {
console.log('will dispatch', action)
return next(action)
}
}
const configureStore = () => {
return createStore(
reducers,
applyMiddleware(logger)
);
};
export default configureStore;
Do not forget to call the applyMiddleware()
function when you pass your function to the store argument.
Principles are exactly the same when you want to use Redux on a React application. However, the react-redux
library brings some cool additional features to fit with React.
Indeed, thanks to this library, you will be able to map your React components props
with the Redux state and actions.
Let's take a concrete case: a Counter
component which could be a component for our previous use case:
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as CounterActions from '../actions/counter';
const Counter = ({ children, value, actions }) => (
<div>
<button>Increment</button>
<button>Decrement</button>
</div>
);
Counter.propTypes = {
children: PropTypes.object.isRequired,
value: PropTypes.number.isRequired,
actions: PropTypes.object.isRequired,
};
const mapStateToProps = state => ({
value: state.counter.current,
});
const mapDispatchToProps = dispatch => ({
actions: bindActionCreators(CounterActions, dispatch),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(Counter);
This way, we are able to retrieve our props values which came from the Redux store but also an actions
property that will allow us to dispatch Redux events when we will call it.
Main things to note here are:
mapStateToProps
is a function that allows to map our Redux state properties with React properties
,mapDispatchToProps
is a function that allows to map Redux actions
with React properties
.These two functions are applied thanks to the connect()
function brought by the react-redux
library.
Note:
We have to use the bindActionCreators()
function over our CounterActions
because this is an object that contains actions functions so this function will allows React to call the dispatch()
Redux function when React will call the functions in order to have them correctly triggered.
If we put in parallel the download numbers of Redux (1 303 720 download over the previous month)
with the 2 334 221 downloads of React
, we can conclude that Redux is today very used
and seems very much appreciated
by developers because it's a simple
solution that can greatly help you to structure your application.
Redux brings, in my opinion, a real solution
to structure complex (or large) business applications and bring that to the React and VueJS (and others) communities.
Author(s)
Vincent Composieux
Architecte passionné par les technologies web depuis de longues années, je pratique principalement du PHP (Symfony) / Javascript mais aussi du Python ou Golang.
You wanna know more about something in particular?
Let's plan a meeting!
Our experts answer all your questions.
Contact usDiscover other content about the same topic
Tutorial on the basics of NextJS for building a website.
You may not be using the React states optimally and I'll explain why
In this article we are going to cover how to set up i18next in your React app