After Implementing Server Side Rendering using NextJS on one of my projects, I noticed that my project is now slow when navigating between pages. If users were to use this, they would wonder if the web app is working or not. With that in mind, I decided to implement a loading animation when navigating between pages.
First, I had to add redux, react-redux, and material-ui to my project using yarn.
yarn add redux
yarn add react-redux
yarn add @material-ui/core
After that, I had to add to my file structure two folders: actions and reducers.
In the actions folder I added an index.js file that contained a function returning an action. Doing this allowed me to name a function that I would use to dispatch a Boolean switch to let my project know that it is loading.
export function setLoadingCondition() {
return {
type: "setLoadingCondition"
}
}
After that I added two files in the reducers folder: loading.js and index.js.
In loading.js, I had to add a reducer (which I named loadingReducer) that has an initial state (that being false) and made sure that it recognized the type: setLoadingCondition I wrote earlier in the index.js file in the actions folder after that function had been dispatched. Once it found it, I made sure that it would return the opposite of the initial state (that being true). If that type had not been dispatched, I made sure that it returned the initial state. In the end, the loading.js file looked like this:
const loadingReducer = (state = false, action) => {
switch(action.type){
case "setLoadingCondition":
return !state
default: return state
}
}
export default loadingReducer
In the index.js file, I had first had to import the loadingReducer I wrote in loading.js and a function called combineReducers from redux. Then I would use the combineReducers function to make sure that when I am selecting a reducer, the loadingReducer could be recognized as just loading for simplicity. That would end up in a const called allReducers, which would be used later. In the end, the index.js file in the reducers folder looked like this:
import loadingReducer from './loading'
import {combineReducers} from 'redux'
const allReducers = combineReducers({
loading: loadingReducer
})
export default allReducers
In the _app.js file I had to make sure that NextJS recognized the fact that my redux and react-redux were being used. First I had to import Provider tag from react-redux. After that, I had put the Component tag within the Provider tag. With the Provider tag wrapped around the Component tag, I had to make sure that Provider was aware of the reducer I created. With that in mind, I had to import createStore from redux and the allReducer I made earlier. In a const I called store, I used the createStore function to utilize allReducer. The store would then be in the Provider tag in an attribute that was also called store. In the end, _app.js looked something like this:
import {createStore} from 'redux'
import allReducer from '../reducers'
import {Provider} from 'react-redux'
export default function MyApp({ Component, pageProps }) {
const store = createStore(allReducer);
return <Provider store={store}><Component {...pageProps} /></Provider>
}
In the components that allow me to navigate between different pages I had to import a hook from react-redux called useDispatch, and setLoadingCondition I made earlier from the index.js file in the actions folder. I then created a const called dispatch which used the useDispatch hook for simplicity reasons. On the element within a Link tag, I added an onClick event allowing me to dispatch the setLoadingCondition function to make sure that project knows that it is loading. On the pages I imported a hook from react-redux called useSelector and a LinearProgress tag from @material-ui/core. In a const I called loading, I used the useSelector hook to select the loadingReducer that was known as loading for simplicity. In the DOM, I made a function that checked to see if the page was loading. If it was then loading would be true. If loading was true, I wrapped a div tag around the LinearProgress tag. Since the amount of data would increase, I had to make sure that the LinearProgress could be seen while scrolling. To do this, I added a className to the div tag so that I could customize it in a css file. Here is what I added to my files:
component:
// import statements
import {useDispatch} from 'react-redux'
import {setLoadingCondition} from '../../actions'
//const
const dispatch = useDispatch()
//DOM
return (
<Link href={`/project?title=${props.id}`}><h1 onClick={() => dispatch(setLoadingCondition())} className={styles.displaytitle}><strong>{props.project}</strong></h1></Link>
)
page:
// import statements
import {useSelector} from 'react-redux'
import {LinearProgress} from '@material-ui/core'
//const
const loading = useSelector(state => state.loading)
//DOM
return (
{loading && <div className="loading"><LinearProgress /></div>}
)
css file:
.loading {
position: fixed;
top: 0;
width: -webkit-fill-available;
width: -moz-available;
z-index: 3;
}
Because of Redux, my project now has a loading animation.