Template React Bootstrap adalah suatu kerangka kerja integrasi react dengan Bootstrap untuk membantu pemula mulai mempelajari aplikasi web melalui tutorial paling dasar.
Pada tutorial kali ini, kita akan membuat aplikasi web yang menampilkan daftar film menggunakan API OMDb. Aplikasi ini akan dapat menerima input kata kunci dan merespon dengan menampilkan film yang sesuai dengan kata kunci. Hal utama yang akan kita jelajahi adalah bagaimana caranya:
- Membuat aplikasi satu halaman.
- Membuat halaman banyak bahasa.
- Interaksi antara react dengan API.
- Unduh dan instal Node v8.10+, npm v5.6+ and Yarn v1.2.0+.
- Unduh dan instal JDK.
- Unduh dan instal Visual Studio Code.
- Membuat akun github.
- Membuat akun netlify.
Project ini menggunakan dasar dari Create React App.
npx create-react-app template-react-bootstrap
React-i18next adalah kerangka internasionalisasi bahasa yang terbaik untuk React / React Native yang didasarkan pada i18next.
yarn add react-i18next i18next i18next-http-backend i18next-browser-languagedetector
Perutean URL deklaratif menggunakan React Router.
yarn add react-router react-router-dom
React-Bootstrap menggantikan Bootstrap JavaScript. Setiap komponen telah dibangun dari awal sebagai komponen React sebenarnya, tanpa dependensi yang tidak diperlukan seperti jQuery. Menginstal dependensi dari library React Bootstrap.
yarn add react-bootstrap bootstrap react-router-bootstrap
BrowserRouter API riwayat HTML5 (pushState, replaceState, dan popstate event) untuk menjaga UI Anda tetap sinkron dengan URL.
import { BrowserRouter as Router } from 'react-router-dom';
...................
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>
Menambahkan aset gambar foto profil pada public/assets/creators.jpg.
Mengkonfigurasi i18n dengan file src/i18n.js dengan konten.
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'id',
debug: true,
interpolation: {
escapeValue: false,
}
});
export default i18n;
Menambahkan file tersebut ke aplikasi melalui src/index.js import './i18n';
.
Menempatkan aset koleksi translasi bahasa pada public/locales/{language_code}/translation.json.
Versi bahasa
{
"header": {
"logo": "React Bootstrap",
"home": "Rumah",
"search": "Pencarian"
},
"homePage": {
"title": "Info kreator",
"description": "Halo, perkenalkan aku william sebagai pembuat template react bootstrap. Aku berharap kode yang telah dibuat dapat membantu orang-orang memulai bekerja dengan react."
},
"searchPage": {
"search": "Cari",
"placeHolder": "Nama film"
}
}
Versi inggris
{
"header": {
"logo": "React Bootstrap",
"home": "Home",
"search": "Search"
},
"homePage": {
"title": "Creators info",
"description": "Hello, I am William as the react bootstrap template creator. I hope the code I created will help people get started working with React."
},
"searchPage": {
"search": "Search",
"placeHolder": "Movie's name"
}
}
Memindahkan file App.* ke folder components.
Pada src/index.js mengganti sumber impor file import App from './components/App';
.
Menginisialisasi file App.js dengan internasionalisasi dan router.
import React, { Suspense } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Switch, Route, Redirect, useLocation } from 'react-router-dom'
function App() {
const location = useLocation();
if (location.pathname === '/') {
return <Redirect to="/home" />;
}
return (
<Suspense fallback="loading">
<div className="App">
<Switch>
<Route path="/">
</Route>
</Switch>
</div>
</Suspense>
);
}
export default App;
Menambahkan Fitur navigasi pada aplikasi melalui src/components/header/index.js.
import React from 'react';
import { Navbar, Nav, NavDropdown } from 'react-bootstrap';
import { useHistory } from 'react-router-dom';
import { LinkContainer } from 'react-router-bootstrap';
import { useTranslation } from 'react-i18next';
import './index.css';
function Header() {
const history = useHistory();
const { t } = useTranslation();
return (
<div className="header">
<Navbar bg="primary" variant="dark">
<Navbar.Brand href="/home">{t('header.logo')}</Navbar.Brand>
<Nav className="mr-auto" activeKey={history.location.pathname}>
<LinkContainer to="/home"><Nav.Link href="/home">{t('header.home')}</Nav.Link></LinkContainer>
<LinkContainer to="/search"><Nav.Link href="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/search">{t('header.search')}</Nav.Link></LinkContainer>
</Nav>
</Navbar>
</div>
);
}
export default Header;
Mengimpor header melalui src/components/App.js.
import Header from './header';
...................
<Route path="/">
<Header />
</Route>
...................
Memilih bahasa dengan menggunakan dropdown bootstrap pada navigasi src/components/header/index.js.
...................
const { t, i18n } = useTranslation();
const handleLanguage = (language) => i18n.changeLanguage(language);
return (
...................
<Navbar.Brand href="/home">{t('header.logo')}</Navbar.Brand>
<Nav className="mr-auto" activeKey={history.location.pathname}>
...................
</Nav>
<NavDropdown className="language ml-auto" title={i18n.language.toUpperCase()} id="nav-dropdown" onSelect={handleLanguage} alignRight>
<NavDropdown.Item eventKey="id">ID</NavDropdown.Item>
<NavDropdown.Item eventKey="en">EN</NavDropdown.Item>
</NavDropdown>
...................
);
}
export default Header;
Beserta file pada navigasi src/components/header/index.css.
.language > a {
color: #FFFFFF;
}
Menambahkan konten pada tiap rute URL pada aplikasi melalui src/components/content/index.js.
import React from 'react';
import { Switch, Route } from 'react-router-dom';
function Content() {
return (
<Container>
<Switch>
<Route path="/home">
</Route>
<Route path="/search">
</Route>
</Switch>
</Container>
);
}
export default Content;
Menambahkan halaman rumah memuat profil diri menggunakan kartu src/components/component/home/index.js.
import React from 'react';
import { Card } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
function HomePage() {
const { t } = useTranslation();
return (
<div>
<Card className="mt-5 mx-auto" style={{ width: '18rem' }}>
<Card.Img variant="top" src={process.env.PUBLIC_URL + "/assets/creators.jpg"} />
<Card.Body>
<Card.Title>{t('homePage.title')}</Card.Title>
<Card.Text>
{t('homePage.description')}
</Card.Text>
</Card.Body>
</Card>
</div>
);
}
export default HomePage;
Mengimpor halaman rumah pada src/components/content/index.js.
import HomePage from '../component/home';
...................
<Route path="/home">
<HomePage />
</Route>
Membuat konteks pencarian src/context/search/reducer.js.
const cases = {
MOVIE_VALUE: (state, payload) => ({
...state,
movie: payload,
})
};
const reducer = (state, action) => {
return cases[action.type](state, action.payload);
};
export default reducer;
Memanggilnya pada src/context/search/index.js.
import React, { createContext, useReducer } from 'react';
import { node } from 'prop-types';
import reducer from './reducer';
const defaultState = {};
export const SearchContext = createContext();
export function SearchProvider(props){
const [state, dispatch] = useReducer(reducer, defaultState);
const {children} = props;
return <SearchContext.Provider value={[state, dispatch]}>{children}</SearchContext.Provider>;
}
SearchProvider.propTypes = {
children: node.isRequired,
};
Membangun komponen untuk melakukan input search dengan useState dan grup input bootstrap src/components/component/search/search.js.
import React, { useContext, useState } from 'react';
import { SearchContext } from '../../../context/search';
import { InputGroup, Button, FormControl, Row, Col } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
function SearchView() {
const { t } = useTranslation();
const [, searchDispatch] = useContext(SearchContext);
const [movieName, setMovieName] = useState('');
const searchInput = () => {
searchDispatch({ type: 'MOVIE_VALUE', payload: movieName})
}
const handleKeyPress = (target) => {
if(target.charCode===13){
searchInput();
}
}
return (
<Row className="mt-3">
<Col md={{ span: 4, offset: 4 }}>
<InputGroup className="mb-3">
<FormControl
placeholder={t('searchPage.placeHolder')}
aria-label={t('searchPage.placeHolder')}
aria-describedby="basic-addon2"
onChange={value => setMovieName(value.target.value)}
onKeyPress={handleKeyPress}
/>
<InputGroup.Append>
<Button onClick={searchInput}>{t('searchPage.search')}</Button>
</InputGroup.Append>
</InputGroup>
</Col>
</Row>
);
}
export default SearchView;
Membangun komponen untuk menampilkan output search dengan useEffect dan fetch api src/components/component/search/movie.js.
import React, { useContext, useEffect, useState } from 'react';
import { SearchContext } from '../../../context/search';
import { Card, Alert, Row, Col, Spinner } from 'react-bootstrap';
function MovieView() {
const [search] = useContext(SearchContext);
const [movie, setMovie] = useState({});
const [isLoading, setLoading] = useState(false);
useEffect(() => {
setMovie({});
if(search.movie){
setLoading(true);
fetch('https://www.omdbapi.com/?i=tt3896198&apikey=1a9ae8c0&t='+search.movie.toLowerCase()).then(res=> res.json())
.then(response => {
setLoading(false);
setMovie(response);
})
.catch(()=>{
setLoading(false);
setMovie({});
});
}
},[search, setMovie, setLoading]);
return (
<Row>
<Col md={{ span: 4, offset: 4 }}>
{isLoading && (<div className="mt-5 d-flex justify-content-center"><Spinner animation="grow" /></div>)}
{movie.Response === "True" && (
<Card>
{movie.Poster !== "N/A" && (
<Card.Img variant="top" src={movie.Poster} alt="none"/>
)}
<Card.Body>
<Card.Title>{movie.Title}</Card.Title>
{movie.Plot !== "N/A" && (
<Card.Text>
{movie.Plot}
</Card.Text>
)}
</Card.Body>
</Card>
)}
{movie.Response === "False" && (
<Alert variant='warning'>
{movie.Error}
</Alert>
)}
</Col>
</Row>
);
}
export default MovieView;
Anda perlu mengganti apikey dan i dari https://www.omdbapi.com/?i=null&apikey=null.
Menambahkan halaman pencarian beserta komponennya pada rute URL src/components/component/search/index.js.
import React from 'react';
import MovieView from './movie';
import SearchView from './search';
import { SearchProvider } from '../../../context/search'
function SearchPage() {
return (
<SearchProvider>
<SearchView />
<MovieView />
</SearchProvider>
);
}
export default SearchPage;
Mengimpor halaman pencarian pada src/components/content/index.js.
import SearchPage from '../component/search';
...................
<Route path="/search">
<SearchPage />
</Route>
Menonaktifkan debug mode pada React-i18next dengan mengubah konfigurasi src/i18n.js debug: false
.
Jika ingin agar aplikasi bekerja offline dan lebih cepat memuat halaman tambahkan PWA di file src/index.js ubah menjadi serviceWorker.register();
.
Menjalankan aplikasi pada mode pengembangan.
Buka https://localhost:3000 untuk melihat halaman di browser.
halaman memuat ulang saat melakukan edit.
kamu juga dapat melihat error pada konsol.
Pada penyebaran aplikasi akan digunakan kerangka dari netlify.
- Login ke akun netlify.
- Klik new site from github.
- Sambungkan dengan akun github.
- Pilih repository yang ingin disebarkan.
- Setting build command
yarn build
dan publish directorybuild/
. - Akses situs contoh pada https://awesome-brattain-372325.netlify.app/.