Persiapan Deploy
Setelah latihan kesegaran otak mari kita lanjut materi kembali, kita akan melakukan deploy ke https://www.netlify.com (tau kenapa?, iya benar GRATISSSSSS).
Sedikit kita lengkapi, untuk halaman login, silahkan buat file page/Login.js
yang isinya seperti berikut.
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { loginUser } from "../actions/user.action";
const Login = (props) => {
const dispatch = useDispatch();
const [user, updateUser] = useState({});
const submitForm = (e) => {
e.preventDefault();
dispatch(loginUser(user));
};
return (
<div className="page">
<h1 className="page-title">Login 30 Hari Js</h1>
<form className="login" onSubmit={(e) => submitForm(e)}>
<label>Email</label>
<input
type="email"
autoComplete={"false"}
placeholder="email"
onChange={(e) => {
updateUser({ ...user, email: e.target.value });
}}
/>
<label>Password</label>
<input
type="password"
autoComplete={"false"}
placeholder="rahasia"
onChange={(e) => {
updateUser({ ...user, password: e.target.value });
}}
/>
<button type="submit" onClick={(e) => submitForm(e)}>
Login
</button>
</form>
</div>
);
};
export default Login;
Ubah sedikit file user.action.js
menjadi seperti berikut.
import axios from "axios";
const baseUrl = `https://warm-refuge-26108.herokuapp.com`;
export const getUsers = () => {
return async (dispatch) => {
dispatch({
type: "GET_USER_REQUEST",
});
try {
const result = await axios.get(baseUrl + `/users`);
dispatch({
type: "GET_USER_DONE",
payload: result.data,
});
} catch (error) {
dispatch({
type: "GET_USER_ERROR",
});
}
};
};
export const createUser = (data) => {
return async (dispatch) => {
try {
const result = await axios.post(baseUrl + `/users`, data);
return result;
} catch (error) {
dispatch({
type: "GET_USER_ERROR",
});
}
};
};
export const loginUser = (data) => {
return async (dispatch) => {
try {
const result = await axios.post(baseUrl + `/users/login`, data);
window.localStorage.setItem("token", result.data.token);
if (result.data.token) {
window.location.href = "/profile";
}
return result;
} catch (error) {
dispatch({
type: "GET_USER_ERROR",
});
}
};
};
export const profileUser = () => {
return async (dispatch) => {
dispatch({
type: "GET_USER_REQUEST",
});
try {
const result = await axios.get(baseUrl + `/users/profile`, {
headers: { token: window.localStorage.getItem("token") },
});
dispatch({
type: "GET_USER_DETAIL_DONE",
payload: result.data,
});
} catch (error) {
dispatch({
type: "GET_USER_ERROR",
});
}
};
};
Ubah juga file user.reducer.js
, dan menjadi seperti berikut.
const initialState = {
data: [],
isError: false,
isLoading: false,
detail: null,
};
export default (state = initialState, action) => {
switch (action.type) {
case "GET_USER_REQUEST":
return {
...state,
isError: false,
isLoading: true,
};
case "GET_USER_DONE":
return {
...state,
data: action.payload,
isError: false,
isLoading: false,
};
case "GET_USER_DETAIL_DONE":
return {
...state,
detail: action.payload,
isError: false,
isLoading: false,
};
case "GET_USER_ERROR":
return {
...state,
isError: true,
isLoading: false,
};
default:
return state;
}
};
Tambahkan file page/Profile.js
, yang isinya seperti berikut.
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { profileUser } from "../actions/user.action";
const Profile = (props) => {
const dispatch = useDispatch();
const usersData = useSelector((state) => state.users);
useEffect(() => {
dispatch(profileUser());
}, []);
if (usersData.detail === null) {
return <h1>Loading...</h1>;
}
return (
<div className="page">
<h1 className="page-title">My Profile</h1>
<div className="profile">
<div className="avatar">
<img src={usersData.detail.picture} />
</div>
<div className="profile-detail">
<div>
<h3> {usersData.detail.name} </h3>
<hr />
<span> {usersData.detail.label} </span>
<p style={{ marginTop: "3rem" }}> {usersData.detail.phone} </p>
<p style={{ marginTop: "1rem" }}> {usersData.detail.email} </p>
<p style={{ marginTop: "1rem" }}> {usersData.detail.website} </p>
<p style={{ marginTop: "3rem" }}>
Summary: {usersData.detail.summary}{" "}
</p>
</div>
</div>
</div>
</div>
);
};
export default Profile;
Ubah file RouterApp.js
untuk mengubah menu sehingga kode menjadi seperti berikut.
import React, { Component, Fragment } from "react";
import { BrowserRouter, Switch, Route, Link } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Profile from "./pages/Profile";
import Register from "./pages/Register";
import Login from "./pages/Login";
class RouterApp extends Component {
state = {
token: null,
};
componentDidMount() {
const token = window.localStorage.getItem("token");
this.setState({ token });
}
render() {
return (
<BrowserRouter>
<Fragment>
<nav>
<li>
{" "}
<Link to="/"> Home </Link>{" "}
</li>
<li>
{" "}
<Link to="/about"> About </Link>{" "}
</li>
{this.state.token === null && (
<>
<li>
{" "}
<Link to="/register"> Register </Link>{" "}
</li>
<li>
{" "}
<Link to="/login"> Login </Link>{" "}
</li>
</>
)}
{this.state.token !== null && (
<>
<li>
{" "}
<Link to="/profile"> Profile </Link>{" "}
</li>
<li>
{" "}
<a
href="#"
onClick={() => {
window.localStorage.removeItem("token");
window.location.href = "/";
}}
>
{" "}
Logout{" "}
</a>{" "}
</li>
</>
)}
</nav>
<main>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" exact component={About} />
{this.state.token !== null && (
<Route path="/profile" exact component={Profile} />
)}
{this.state.token === null && (
<>
<Route path="/register" exact component={Register} />
<Route path="/login" exact component={Login} />
</>
)}
</Switch>
</main>
</Fragment>
</BrowserRouter>
);
}
}
export default RouterApp;
Agar terlihat elegan tambahkan css pada file App.css
yang isinya seperti berikut.
* {
margin: 0;
padding: 0;
text-decoration: none;
list-style: none;
}
body {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
min-height: 100vh;
}
body nav {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
background: green;
width: 100%;
height: 40px;
}
body nav li {
margin-right: 2rem;
}
body nav li a {
color: white;
}
body nav li a:hover {
color: yellow;
}
body main {
padding: 3rem;
color: gray;
}
.page {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
}
.page .page-title {
color: black;
text-align: center;
margin-bottom: 1rem;
}
.page .users {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: start;
-ms-flex-pack: start;
justify-content: flex-start;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.page .users .user {
width: calc(calc(100% / 4) - 1rem);
border: 1px green solid;
margin-right: 1rem;
margin-bottom: 1rem;
}
.page .users .user:nth-child(4n) {
margin-right: 0;
}
.page .users .user .user-img-container img {
width: 100%;
-o-object-fit: cover;
object-fit: cover;
}
.page .users .user .user-detail h3 {
text-align: center;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.page .register,
.page .login {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
min-width: 80%;
margin: auto;
}
.page .register input,
.page .register textarea,
.page .login input,
.page .login textarea {
margin-bottom: 1rem;
padding: 0.5rem;
outline: none;
}
.page .register input:focus,
.page .register textarea:focus,
.page .login input:focus,
.page .login textarea:focus {
border: 1px solid green;
}
.page .register button,
.page .login button {
color: #ffffff;
background: green;
padding: 0.5rem;
border: none;
border-radius: 4px;
cursor: pointer;
}
.page .register button:hover,
.page .login button:hover {
background: #ffffff;
color: green;
border: 1px solid green;
}
.page .profile {
width: 800px;
min-width: 800px;
max-width: 800px;
padding: 2rem;
border: 1px dotted black;
margin: auto;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.page .profile .avatar {
margin-right: 1rem;
}
.page .profile .avatar img {
width: 200px;
border-radius: 50%;
border: 1px solid red;
}
Source cek disini
Deploy
Pertama sekali, silahkan login ke netlify atau jika belum memiliki akun silahkan daftar terlebih dahulu.
Kemudian pada proyek react build untuk di upload ke hosting dengan perintah berikut.
npm run build
Maka akan menghasilkan file compress dari proyek react tersebut.
Selanjutnya folder build drag ke netlify seperti berikut.
Setelah di drag, maka netlify memberikan url yang dapat kita gunakan sebagai alamat web kita.
Jika ingin mengubah url, netlify menyediakannya melalui custom domain.