fullstack web app-golang #4

f

so lets start building the front end of our app. As I said we’re gonna use react here.

This is our project structure:

I think I gave all the files a good descriptive name. I’ll explain things along the way.

But before doing anything. we’re gonna need 2 libraries. materialize-css and react-router.

so install it with npm.

materialize css

To use materialize css you have to import it and initialize it in your useEffect()

import 'materialize-css/dist/css/materialize.min.css'
import M from 'materialize-css/dist/js/materialize.min.js'
useEffect(() => {
	M.AutoInit()
}, [])

state management

For state management, we’re gonna use context api. we can also use redux, but i think its a over kill for our project, that barely have any states.

So first lets finish our state management part.

In the types.js, we simply have all constants

export const ADD_POST = 'ADD_POST'
export const REGISTER_SUCCESS = 'REGISTER_SUCCESS'
export const REGISTER_FAIL = 'REGISTER_FAIL'
export const USER_LOADED = 'USER_LOADED'
export const AUTH_ERROR = 'AUTH_ERROR'
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
export const LOGIN_FAIL = 'LOGIN_FAIL'
export const LOGOUT = 'LOGOUT'
export const CLEAR_ERRORS = 'CLEAR_ERRORS'

AuthContext.js is nothing new.

import { createContext } from 'react'

const authContext = createContext()

export default authContext

For auth reducer,


export default (state, action) => {
	switch (action.type) {
		case USER_LOADED:
			return {
				...state,
				isAuthenticated: true,
				loading: false,
			}
		case REGISTER_SUCCESS:
		case LOGIN_SUCCESS:
			localStorage.setItem('token', action.payload)
			return {
				...state,
				...action.payload,
				isAuthenticated: true,
				loading: false,
			}
		case REGISTER_FAIL:
		case AUTH_ERROR:
		case LOGIN_FAIL:
		case LOGOUT:
			localStorage.removeItem('token')
			return {
				...state,
				token: null,
				isAuthenticated: false,
				loading: false,
				user: null,
				error: action.payload,
			}
		case CLEAR_ERRORS:
			return {
				...state,
				error: null,
			}
		default:
			return state
	}
}

Its just a bunch of cases in switch statement. We’re changing the state as usual.

The key thing here is

for case REGISTER_SUCCESS and case LOGIN_SUCCESS:

we setted the token in our localstorage

And for case LOGOUT:

we removed that token from localstorage

authstate:

const BASE_URL = 'http://localhost:8080'

use useReducer()

const initialState = {
	token: localStorage.getItem('token'),
	isAuthenticated: null,
	loading: true,
	error: null,
}

const [state, dispatch] = useReducer(authReducer, initialState)

our return statement is as expected. we’re just gonna pass some bunch of state and handlers.

	return (
		<AuthContext.Provider
			value={{
				token: state.token,
				isAuthenticated: state.isAuthenticated,
				loading: state.loading,
				error: state.error,
				register,
				loadUser,
				login,
				logout,
				clearErrors,
			}}
		>
			{props.children}
		</AuthContext.Provider>
	)

load user

this sets the token to the localstorage. and also changes the state.

	const loadUser = async () => {
		if (localStorage.token) {
			setAuthToken(localStorage.token)

			dispatch({
				type: USER_LOADED,
			})
		}
	}

register

const register = async formData => {
		const config = {
			headers: {
				'Content-Type': 'application/json',
			},
		}

		try {
			const res = await axios.post(`${BASE_URL}/register`, formData, config)
			console.log(res)

			dispatch({
				type: REGISTER_SUCCESS,
				payload: res.data,
			})

			loadUser()
		} catch (err) {
			dispatch({
				type: REGISTER_FAIL,
				payload: 'failed',
			})
		}
	}

If you’re new to axios, its no different than the usual fetch request. Except we dont wanna use res.json(). we can simply get the data by res.data.

login user


	// Login User
	const login = async formData => {
		const config = {
			headers: {
				'Content-Type': 'application/json',
			},
		}

		try {
			const res = await axios.post(`${BASE_URL}/login`, formData, config)

			dispatch({
				type: LOGIN_SUCCESS,
				payload: res.data,
			})

			loadUser()
		} catch (err) {
			dispatch({
				type: LOGIN_FAIL,
				payload: 'failed',
			})
		}
	}

logout and clear errors

	// Logout
	const logout = () => dispatch({ type: LOGOUT })

	// Clear Errors
	const clearErrors = () => dispatch({ type: CLEAR_ERRORS })

And thats all the state management we’re gonna use.

utils/setAuthToken.js

This is actually a helper function. As we know we need to pass the token everytime whenever we call /addpost, cause only authenticated users can post data. So this fuction will add it automatically.

import axios from 'axios'

const setAuthToken = token => {
	if (token) {
		axios.defaults.headers.common['Token'] = token
	} else {
		delete axios.defaults.headers.common['Token']
	}
}

export default setAuthToken

use this in your main js file

import setAuthToken from './utils/setAuthToken'

if (localStorage.token) {
	setAuthToken(localStorage.token)
}

If you guys don’t mind paste this css in your app.css file. These are just some basic margin and padding styles.

/* div{
    border: thin red dotted;
} */

#logo {
	font-family: 'Mansalva';
	font-size: 26pt;
}

.webname {
	font-family: 'Mansalva';
}

.col {
	margin-top: 20px;
}

.btn-group {
	margin-top: 40px;
}

.card-body {
	margin-top: 60px;
}

.mycard {
	margin-top: 40px !important;
}

We also need some google fonts and icons. So open your index.html. and paste these there


    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css?family=Fira+Sans|Mansalva|Pacifico&display=swap" rel="stylesheet">

And I think thats enough for this article.

Happy coding 🙂

About the author

vigneshwar

Add comment

Leave a Reply

By vigneshwar

Most common tags

%d bloggers like this: