Untuk memperdalam pemahaman tentang **Redux**, kita bisa menambahkan beberapa fitur pada aplikasi yang telah dibuat sebelumnya. Fitur-fitur tambahan ini akan memperkenalkan konsep penting dalam **Redux** seperti **multiple slices**, **middleware**, dan **handling async actions** menggunakan **Redux Toolkit**.
### Fitur Tambahan:
1. **Fitur Reset Counter**: Tombol untuk mengatur ulang nilai counter ke 0.
2. **Fitur Async Fetch**: Mengambil data dari API eksternal secara asinkron dan menampilkannya.
3. **Fitur Toggle Theme**: Tombol untuk mengubah tema antara mode terang (light) dan mode gelap (dark).
Berikut adalah langkah-langkah penambahan fitur:
### 1. **Fitur Reset Counter**
Menambahkan tombol untuk mereset nilai counter ke 0.
#### Langkah 1: Tambahkan Reducer `reset` di `counterSlice.js`
Buka file `counterSlice.js` dan tambahkan reducer baru bernama `reset`.
```javascript
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
reset: (state) => {
state.value = 0; // Mengatur nilai kembali ke 0
},
},
});
export const { increment, decrement, reset } = counterSlice.actions;
export default counterSlice.reducer;
#### Langkah 2: Tambahkan Tombol Reset di `Counter.js`
Buka file `Counter.js` dan tambahkan tombol untuk mereset counter.
```javascript
function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div className="counter">
<h1>Counter: {count}</h1>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(reset())}>Reset</button> {/* Tombol Reset */}
</div>
);
}
#### Langkah 3: Tambahkan CSS untuk Tombol Reset (Opsional)
Jika kamu ingin menambahkan gaya untuk tombol reset, tambahkan di `Counter.css`.
```css
.counter button {
font-size: 20px;
padding: 10px 20px;
margin: 5px;
border: none;
background-color: #007bff;
color: white;
cursor: pointer;
}
.counter button:hover {
background-color: #0056b3;
}
.counter button:last-child {
background-color: #dc3545; /* Tombol reset berwarna merah */
}
.counter button:last-child:hover {
background-color: #c82333;
}
### 2. **Fitur Async Fetch**: Menampilkan Data Asinkron dari API
Untuk ini, kita akan menggunakan Redux Thunk (bawaan dari Redux Toolkit) untuk menangani operasi asinkron.
#### Langkah 1: Buat Slice Baru untuk Fetch Data (`dataSlice.js`)
Buat file baru bernama `dataSlice.js` di folder `redux`.
```javascript
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// Thunk untuk mengambil data dari API
export const fetchData = createAsyncThunk(
'data/fetchData',
async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const data = await response.json();
return data;
}
);
const dataSlice = createSlice({
name: 'data',
initialState: {
item: null,
status: 'idle',
error: null,
},
extraReducers: (builder) => {
builder
.addCase(fetchData.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchData.fulfilled, (state, action) => {
state.status = 'succeeded';
state.item = action.payload;
})
.addCase(fetchData.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
},
});
export default dataSlice.reducer;
#### Langkah 2: Update `store.js` untuk Menambahkan `dataSlice`
Buka file `store.js` dan tambahkan `dataReducer` dari `dataSlice.js`.
```javascript
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
import dataReducer from './dataSlice'; // Import reducer baru
export const store = configureStore({
reducer: {
counter: counterReducer,
data: dataReducer, // Tambahkan reducer baru
},
});
#### Langkah 3: Buat Komponen `DataFetcher.js` untuk Mengambil Data
Buat file `DataFetcher.js` di folder `components` untuk menampilkan data yang diambil.
```javascript
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchData } from '../redux/dataSlice';
function DataFetcher() {
const dispatch = useDispatch();
const data = useSelector((state) => state.data.item);
const status = useSelector((state) => state.data.status);
useEffect(() => {
dispatch(fetchData());
}, [dispatch]);
return (
<div>
<h2>Data Fetcher</h2>
{status === 'loading' && <p>Loading...</p>}
{status === 'succeeded' && <pre>{JSON.stringify(data, null, 2)}</pre>}
{status === 'failed' && <p>Error fetching data.</p>}
</div>
);
}
export default DataFetcher;
#### Langkah 4: Tambahkan `DataFetcher` ke `App.js`
Terakhir, impor dan gunakan komponen ini di dalam `App.js`.
```javascript
import React from 'react';
import Counter from './components/Counter';
import DataFetcher from './components/DataFetcher';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<h1>Redux Advanced Features</h1>
<Counter />
<DataFetcher /> {/* Komponen Fetch Data */}
</header>
</div>
);
}
export default App;
### 3. **Fitur Toggle Theme (Mode Terang dan Gelap)**
Untuk membuat toggle tema, kita bisa menggunakan state Redux untuk menyimpan mode tema.
#### Langkah 1: Buat `themeSlice.js`
Buat file baru `themeSlice.js` di folder `redux` untuk mengelola state tema.
```javascript
import { createSlice } from '@reduxjs/toolkit';
const themeSlice = createSlice({
name: 'theme',
initialState: {
darkMode: false,
},
reducers: {
toggleTheme: (state) => {
state.darkMode = !state.darkMode;
},
},
});
export const { toggleTheme } = themeSlice.actions;
export default themeSlice.reducer;
#### Langkah 2: Tambahkan `themeReducer` ke `store.js`
Buka `store.js` dan tambahkan reducer dari `themeSlice`.
```javascript
import themeReducer from './themeSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
data: dataReducer,
theme: themeReducer, // Tambahkan themeReducer
},
});
#### Langkah 3: Tambahkan Tombol Toggle Tema di `App.js`
Edit file `App.js` untuk menambahkan tombol toggle tema.
```javascript
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Counter from './components/Counter';
import DataFetcher from './components/DataFetcher';
import { toggleTheme } from './redux/themeSlice';
import './App.css';
function App() {
const dispatch = useDispatch();
const darkMode = useSelector((state) => state.theme.darkMode);
return (
<div className={`App ${darkMode ? 'dark-mode' : ''}`}>
<header className="App-header">
<h1>Redux Advanced Features</h1>
<Counter />
<DataFetcher />
<button onClick={() => dispatch(toggleTheme())}>
{darkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
</button>
</header>
</div>
);
}
export default App;
#### Langkah 4: Tambahkan CSS untuk Tema Gelap
Terakhir, tambahkan beberapa gaya CSS untuk tema gelap di `App.css`.
```css
.App.dark-mode {
background-color: #333;
color: white;
}
.App-header.dark-mode {
background-color: #222;
}
### Fitur Tambahan:
1. **Fitur Reset Counter**: Tombol untuk mengatur ulang nilai counter ke 0.
2. **Fitur Async Fetch**: Mengambil data dari API eksternal secara asinkron dan menampilkannya.
3. **Fitur Toggle Theme**: Tombol untuk mengubah tema antara mode terang (light) dan mode gelap (dark).
Berikut adalah langkah-langkah penambahan fitur:
### 1. **Fitur Reset Counter**
Menambahkan tombol untuk mereset nilai counter ke 0.
#### Langkah 1: Tambahkan Reducer `reset` di `counterSlice.js`
Buka file `counterSlice.js` dan tambahkan reducer baru bernama `reset`.
```javascript
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
reset: (state) => {
state.value = 0; // Mengatur nilai kembali ke 0
},
},
});
export const { increment, decrement, reset } = counterSlice.actions;
export default counterSlice.reducer;
#### Langkah 2: Tambahkan Tombol Reset di `Counter.js`
Buka file `Counter.js` dan tambahkan tombol untuk mereset counter.
```javascript
function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div className="counter">
<h1>Counter: {count}</h1>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(reset())}>Reset</button> {/* Tombol Reset */}
</div>
);
}
#### Langkah 3: Tambahkan CSS untuk Tombol Reset (Opsional)
Jika kamu ingin menambahkan gaya untuk tombol reset, tambahkan di `Counter.css`.
```css
.counter button {
font-size: 20px;
padding: 10px 20px;
margin: 5px;
border: none;
background-color: #007bff;
color: white;
cursor: pointer;
}
.counter button:hover {
background-color: #0056b3;
}
.counter button:last-child {
background-color: #dc3545; /* Tombol reset berwarna merah */
}
.counter button:last-child:hover {
background-color: #c82333;
}
### 2. **Fitur Async Fetch**: Menampilkan Data Asinkron dari API
Untuk ini, kita akan menggunakan Redux Thunk (bawaan dari Redux Toolkit) untuk menangani operasi asinkron.
#### Langkah 1: Buat Slice Baru untuk Fetch Data (`dataSlice.js`)
Buat file baru bernama `dataSlice.js` di folder `redux`.
```javascript
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// Thunk untuk mengambil data dari API
export const fetchData = createAsyncThunk(
'data/fetchData',
async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const data = await response.json();
return data;
}
);
const dataSlice = createSlice({
name: 'data',
initialState: {
item: null,
status: 'idle',
error: null,
},
extraReducers: (builder) => {
builder
.addCase(fetchData.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchData.fulfilled, (state, action) => {
state.status = 'succeeded';
state.item = action.payload;
})
.addCase(fetchData.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
},
});
export default dataSlice.reducer;
#### Langkah 2: Update `store.js` untuk Menambahkan `dataSlice`
Buka file `store.js` dan tambahkan `dataReducer` dari `dataSlice.js`.
```javascript
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
import dataReducer from './dataSlice'; // Import reducer baru
export const store = configureStore({
reducer: {
counter: counterReducer,
data: dataReducer, // Tambahkan reducer baru
},
});
#### Langkah 3: Buat Komponen `DataFetcher.js` untuk Mengambil Data
Buat file `DataFetcher.js` di folder `components` untuk menampilkan data yang diambil.
```javascript
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchData } from '../redux/dataSlice';
function DataFetcher() {
const dispatch = useDispatch();
const data = useSelector((state) => state.data.item);
const status = useSelector((state) => state.data.status);
useEffect(() => {
dispatch(fetchData());
}, [dispatch]);
return (
<div>
<h2>Data Fetcher</h2>
{status === 'loading' && <p>Loading...</p>}
{status === 'succeeded' && <pre>{JSON.stringify(data, null, 2)}</pre>}
{status === 'failed' && <p>Error fetching data.</p>}
</div>
);
}
export default DataFetcher;
#### Langkah 4: Tambahkan `DataFetcher` ke `App.js`
Terakhir, impor dan gunakan komponen ini di dalam `App.js`.
```javascript
import React from 'react';
import Counter from './components/Counter';
import DataFetcher from './components/DataFetcher';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<h1>Redux Advanced Features</h1>
<Counter />
<DataFetcher /> {/* Komponen Fetch Data */}
</header>
</div>
);
}
export default App;
### 3. **Fitur Toggle Theme (Mode Terang dan Gelap)**
Untuk membuat toggle tema, kita bisa menggunakan state Redux untuk menyimpan mode tema.
#### Langkah 1: Buat `themeSlice.js`
Buat file baru `themeSlice.js` di folder `redux` untuk mengelola state tema.
```javascript
import { createSlice } from '@reduxjs/toolkit';
const themeSlice = createSlice({
name: 'theme',
initialState: {
darkMode: false,
},
reducers: {
toggleTheme: (state) => {
state.darkMode = !state.darkMode;
},
},
});
export const { toggleTheme } = themeSlice.actions;
export default themeSlice.reducer;
#### Langkah 2: Tambahkan `themeReducer` ke `store.js`
Buka `store.js` dan tambahkan reducer dari `themeSlice`.
```javascript
import themeReducer from './themeSlice';
export const store = configureStore({
reducer: {
counter: counterReducer,
data: dataReducer,
theme: themeReducer, // Tambahkan themeReducer
},
});
#### Langkah 3: Tambahkan Tombol Toggle Tema di `App.js`
Edit file `App.js` untuk menambahkan tombol toggle tema.
```javascript
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import Counter from './components/Counter';
import DataFetcher from './components/DataFetcher';
import { toggleTheme } from './redux/themeSlice';
import './App.css';
function App() {
const dispatch = useDispatch();
const darkMode = useSelector((state) => state.theme.darkMode);
return (
<div className={`App ${darkMode ? 'dark-mode' : ''}`}>
<header className="App-header">
<h1>Redux Advanced Features</h1>
<Counter />
<DataFetcher />
<button onClick={() => dispatch(toggleTheme())}>
{darkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
</button>
</header>
</div>
);
}
export default App;
#### Langkah 4: Tambahkan CSS untuk Tema Gelap
Terakhir, tambahkan beberapa gaya CSS untuk tema gelap di `App.css`.
```css
.App.dark-mode {
background-color: #333;
color: white;
}
.App-header.dark-mode {
background-color: #222;
}
Dengan fitur tambahan ini, kamu akan memahami lebih dalam bagaimana Redux mengelola state, baik itu untuk **counter sederhana**, **data asinkron**, maupun **mode tema**. Fitur-fitur ini juga memperkenalkan beberapa konsep penting seperti **middleware** dan **handling async actions** dalam Redux.