目前這個系列已挑選ChatGPT、前端&React相關章節出版成書!
感謝大家的閱讀與支持!
《用 ChatGPT 詠唱來點亮 React & 前端技能樹》
歷經重新整理、校正、編排、與修改後,讓章節內容之間的銜接更平順,除了補充上更多的解說之外,甚至有部分章節為重新撰寫的內容,讓理解變得更輕鬆!
目前已在天瓏書局、博客來、金石堂等通路都買得到了! 也有電子書的選擇!
歡迎各位點擊連結選購,謝謝大家~
上一篇中,在建立reducer和Action的時候
有那麼億…點點麻煩啊
雖然說,若用建立常數變數的方式,可以維護action的type和reducer內的case拼字正確
但也僅於此而已
直接來問問ChatGPT有沒有更簡潔、簡單上手的方式,可以將兩者一起管理、建立
當你在React Redux中建立reducer和action時,可以使用一些簡潔且簡單上手的方法,特別是當你使用TypeScript時。以下是一個示範,我們將使用Redux Toolkit,這是一個官方提供的工具,能夠簡化Redux的開發流程。
首先,確保你已經安裝了redux和@reduxjs/toolkit:
npm install redux @reduxjs/toolkit
接下來,我們將創建一個Redux模塊,其中包含reducer和action。
- 創建一個新的檔案,例如
counterSlice.ts
,這將是我們的Redux模塊。// counterSlice.ts import { createSlice } from '@reduxjs/toolkit'; // 初始狀態 const initialState = { count: 0, }; // 創建一個Slice const counterSlice = createSlice({ name: 'counter', initialState, reducers: { increment: (state) => { state.count += 1; }, decrement: (state) => { state.count -= 1; }, }, }); // 匯出actions和reducer export const { increment, decrement } = counterSlice.actions; export default counterSlice.reducer;
- 在你的Redux store中使用這個模塊。
// store.ts import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './counterSlice'; const store = configureStore({ reducer: { counter: counterReducer, }, }); export default store;
現在你已經設定好了Redux,可以在React組件中使用它。
- 在React組件中使用Redux。
// Counter.js import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { increment, decrement } from './counterSlice'; function Counter() { const count = useSelector((state) => state.counter.count); const dispatch = useDispatch(); return ( <div> <p>計數:{count}</p> <button onClick={() => dispatch(increment())}>增加</button> <button onClick={() => dispatch(decrement())}>減少</button> </div> ); } export default Counter;
這是一個簡單的範例,使用Redux Toolkit簡化了Redux的設定和使用過程。它讓你能夠集中精力於編寫邏輯,而不必擔心繁瑣的Redux配置。希望這有助於你更輕鬆地管理Redux的reducer和action。
耶,ChatGPT直接給Redux Toolkit的安裝方式
還有用了一個我們沒看過的 createSlice
這個function
請ChatGPT提供有關這個Redux Toolkit的教學與介紹
當然,我很樂意為你提供Redux Toolkit的基本教學!Redux Toolkit是Redux官方提供的一個工具集,旨在簡化Redux的使用和開發流程。以下是一個簡單的步驟指南,讓你可以快速上手Redux Toolkit。
步驟 1:安裝 Redux Toolkit
首先,你需要確保已經安裝了redux和@reduxjs/toolkit:
npm install redux @reduxjs/toolkit
步驟 2:建立 Redux Slice
在Redux Toolkit中,你可以使用Slice來管理reducer和action。Slice是一個包含了reducer和action的單元,可以用來處理特定部分的應用狀態。
- 創建一個新的檔案,例如
counterSlice.ts
,這將是我們的Redux模塊。// counterSlice.ts import { createSlice } from '@reduxjs/toolkit'; // 初始狀態 const initialState = { count: 0, }; // 創建一個Slice const counterSlice = createSlice({ name: 'counter', initialState, reducers: { increment: (state) => { state.count += 1; }, decrement: (state) => { state.count -= 1; }, }, }); // 匯出actions和reducer export const { increment, decrement } = counterSlice.actions; export default counterSlice.reducer;
步驟 3:建立 Redux Store
在Redux中,你需要有一個store來管理整個應用的狀態。
// store.ts import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './counterSlice'; const store = configureStore({ reducer: { counter: counterReducer, }, }); export default store;
步驟 4:在React中使用Redux
現在,你可以在React組件中使用Redux來管理狀態。
// Counter.js import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { increment, decrement } from './counterSlice'; function Counter() { const count = useSelector((state) => state.counter.count); const dispatch = useDispatch(); return ( <div> <p>計數:{count}</p> <button onClick={() => dispatch(increment())}>增加</button> <button onClick={() => dispatch(decrement())}>減少</button> </div> ); } export default Counter;
步驟 5:在應用中使用Redux Store
在你的應用的入口處,通常是
index.js
或App.js
中,將Redux Store提供給整個應用。// index.js 或 App.js import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import store from './store'; import App from './App'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
這樣就完成了Redux Toolkit的基本設置和使用。希望這能幫助你快速上手Redux Toolkit!如果你有任何進一步的問題或需要更多的幫助,請隨時告訴我。
OK,我們看到了ChatGPT給了一段跟一開始幾乎一樣的範例,只是有加上 App.js
內的串接
直接針對我們陌生的function createSlice
進行詢問好了
當然!
createSlice
是 Redux Toolkit 提供的一個函式,它可以幫助你創建一個包含了 reducer 和 action 的 Slice,讓你更方便地管理 Redux 的狀態。以下是
createSlice
的更進一步介紹:
createSlice
的基本結構const slice = createSlice({ name: 'sliceName', // 這是 Slice 的名稱,也會成為 state 的屬性名稱 initialState: {}, // 初始狀態 reducers: { actionName: (state, action) => { // 在這裡定義 action 所對應的邏輯 }, // 可以添加更多的 reducers }, });
屬性說明
name
: 這是 Slice 的名稱,也會成為 state 的屬性名稱。在上面的例子中,sliceName
就是 Slice 的名稱。initialState
: 這是 Slice 的初始狀態。它是一個 JavaScript 物件,包含了 Slice 中所有的初始值。reducers
: 這是一個物件,其中包含了所有的 reducers。每個 reducer 都是一個 function,它接收兩個參數:state
和action
。在 reducer 中,你可以根據 action 的 type 來更新 state。返回值
createSlice
會返回一個包含了自動產生的 action creators 和 reducer 的物件。你可以通過物件的屬性來使用它們。例如,在上面的例子中,
slice.actions
就包含了兩個自動產生的 action creators:actionName
和actionName2
。使用 Slice
在你的應用中,你可以透過以下方式使用 Slice:
import { createSlice } from '@reduxjs/toolkit'; const slice = createSlice({ name: 'counter', initialState: { count: 0, }, reducers: { increment: (state) => { state.count += 1; }, decrement: (state) => { state.count -= 1; }, }, }); export const { increment, decrement } = slice.actions; export default slice.reducer;
上述例子中,我們創建了一個名為
counter
的 Slice,它包含了兩個 reducers:increment
和decrement
。然後我們將這些 action creators 匯出,以便在其他地方使用。使用在 Redux Store 中
在 Redux Store 中,你可以這樣使用這個 Slice:
import { configureStore } from '@reduxjs/toolkit'; import counterReducer from './counterSlice'; const store = configureStore({ reducer: { counter: counterReducer, }, }); export default store;
這樣,你就可以在應用中使用這個 Slice 了。
希望這能幫助你更深入地了解
createSlice
的使用方式!如果你有任何進一步的問題,請隨時告訴我。
第三次的範例,終於好好看著createSlice
,會發現它裡面定義的reducers中,包含了這一段
increment: (state) => {
state.count += 1;
},
會發現欸?欸欸欸? 他直接更改了state的值了耶??
我們在上一篇有和ChatGPT確認,reducer必須保持 pure 與immutable
來問問ChatGPT有關這個問題
在Redux Toolkit的
createSlice
中的reducers可以直接更動state的值是因為Redux Toolkit使用了一個稱為"Immer"的庫,這個庫允許你以一種看似直接修改state的方式來編寫reducers,但實際上是通過複製state並生成新的immutable(不可變)版本來實現的。這樣可以確保Redux的不可變性原則得以遵守,同時使reducers的編寫更加簡單和直觀。當你在一個reducer中對state進行更改時,實際上是Immer在後台處理這些更改,然後返回一個新的immutable的state。這使得你無需手動複製state,而只需關注要如何更新它,讓代碼更容易理解和維護。
以下是一個簡單的示例,展示了如何使用Immer在reducers中更新state:
import { createSlice } from '@reduxjs/toolkit'; const counterSlice = createSlice({ name: 'counter', initialState: { count: 0, }, reducers: { increment: (state) => { // 使用Immer來更新state state.count += 1; }, decrement: (state) => { // 使用Immer來更新state state.count -= 1; }, }, });
這樣的代碼看起來就像是直接修改了state的值,但實際上Redux Toolkit會在背後處理它,確保生成一個新的immutable的state,以符合Redux的設計原則。
總之,Redux Toolkit的
createSlice
結合了Immer庫,讓reducers的編寫變得簡單且直觀,同時保持了Redux中的不可變性。这是Redux Toolkit的一個非常方便的特性。
好的,原來結合了Immer呀,所以可以直接更改沒關係,大大簡化了reducer的寫法
在另外,上面的範例裡面,都沒有傳入參數並使用參數的部分
請ChatGPT示範一下
在
createSlice
中的reducers中接受傳入參數,可以透過在action creator中返回一個函式,這個函式將接受傳入的參數並返回一個action物件。以下是一個示例:import { createSlice } from '@reduxjs/toolkit'; const counterSlice = createSlice({ name: 'counter', initialState: { count: 0, }, reducers: { incrementByAmount: (state, action) => { state.count += action.payload; // 使用action.payload來獲取傳入的參數 }, }, }); export const { incrementByAmount } = counterSlice.actions; export default counterSlice.reducer;
在這個示例中,我們創建了一個名為
incrementByAmount
的action creator,它接受一個參數amount
。在reducer中,我們使用action.payload
來獲取傳入的參數amount
,並將它加到state中。接下來,你可以在你的應用中使用這個action creator:
import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { incrementByAmount } from './counterSlice'; function Counter() { const count = useSelector((state) => state.counter.count); const dispatch = useDispatch(); const handleIncrement = () => { dispatch(incrementByAmount(5)); // 傳入參數5 }; return ( <div> <p>計數:{count}</p> <button onClick={handleIncrement}>增加 5</button> </div> ); } export default Counter;
在這個例子中,當按下按鈕時,會呼叫
handleIncrement
函式,並調用dispatch(incrementByAmount(5))
來傳入參數5。這樣就完成了在
createSlice
中的reducers中接受傳入參數的示例。希望這對你有所幫助!如果你有任何進一步的問題,請隨時告訴我。
了解用法之後,搭配上一篇的範例,用CreateSlice的方式,建立TODO List好了
當然,讓我們用Redux Toolkit的
createSlice
來實現一個簡單的TODO List範例。首先,安裝Redux和Redux Toolkit:
npm install redux @reduxjs/toolkit
接著,創建一個新的檔案
todoSlice.js
:import { createSlice } from '@reduxjs/toolkit'; const todoSlice = createSlice({ name: 'todos', initialState: { list: [], }, reducers: { addTodo: (state, action) => { state.list.push({ id: Date.now(), text: action.payload, completed: false }); }, toggleTodo: (state, action) => { const todo = state.list.find(item => item.id === action.payload); if (todo) { todo.completed = !todo.completed; } }, removeTodo: (state, action) => { state.list = state.list.filter(item => item.id !== action.payload); }, }, }); export const { addTodo, toggleTodo, removeTodo } = todoSlice.actions; export default todoSlice.reducer;
這裡我們創建了一個名為
todos
的Slice,它有三個reducers:addTodo
用於新增一個TODO,toggleTodo
用於切換TODO的完成狀態,removeTodo
用於移除一個TODO。接下來,在你的應用中使用這個Slice,並在畫面上顯示TODO List:
import React, { useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { addTodo, toggleTodo, removeTodo } from './todoSlice'; function App() { const todos = useSelector(state => state.todos.list); const dispatch = useDispatch(); const [newTodo, setNewTodo] = useState(''); const handleAddTodo = () => { if (newTodo.trim() !== '') { dispatch(addTodo(newTodo)); setNewTodo(''); } }; return ( <div> <h1>TODO List</h1> <ul> {todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => dispatch(toggleTodo(todo.id))} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> <button onClick={() => dispatch(removeTodo(todo.id))}>刪除</button> </li> ))} </ul> <div> <input type="text" value={newTodo} onChange={(e) => setNewTodo(e.target.value)} /> <button onClick={handleAddTodo}>新增</button> </div> </div> ); } export default App;
這個範例中,我們使用了React和Redux Toolkit,創建了一個簡單的TODO List應用。你可以在畫面上新增、刪除和切換TODO的完成狀態。
希望這個範例對你有所幫助!如果你有任何進一步的問題,請隨時告訴我。
建立上簡潔許多,使用的時候也不會需要傳入action type的常數字串
不用建立 Action Creators 等等,真的方便很多
讓ChatGPT幫我們統整兩者差異與優劣
使用 Redux Toolkit 和不使用 Redux Toolkit 各有其優劣,具體取決於你的應用和團隊的需求。以下是兩者的優劣點比較:
使用 Redux Toolkit 的優點:
- 簡化 Redux 開發流程:
- Redux Toolkit 提供了一個簡潔且直觀的方式來定義 reducers 和 actions,減少了繁瑣的模板代碼。
- 自動生成 actions 和 reducers:
createSlice
可以自動生成 reducers 和 action creators,省去了手動定義的步驟,減少了重複性代碼。- Immer 整合:
- Redux Toolkit 整合了 Immer,使得在 reducers 中更動 state 變得直觀且容易理解,同時保持了 Redux 的不可變性原則。
- 更優雅的代碼:
- Redux Toolkit 提供了一個更優雅、簡潔的方式來編寫 Redux 代碼,讓開發人員可以更專注於業務邏輯。
- DevTools 整合:
- Redux Toolkit 預設整合了 Redux DevTools,提供了一個方便的開發者工具來偵錯和監控應用的狀態變化。
使用 Redux Toolkit 的缺點:
- 學習成本:
- 如果你已經熟悉傳統的 Redux,學習並適應 Redux Toolkit 可能需要一些時間。
不使用 Redux Toolkit 的優點:
- 更細緻的控制:
- 不使用 Redux Toolkit 可以讓你對 Redux 的每個部分進行更細緻的控制,對於一些特殊需求可能更靈活。
- 更清晰的理解 Redux 核心概念:
- 不使用 Redux Toolkit 可以讓你更深入地理解 Redux 的核心概念,這在某些情況下可能是一個優點。
不使用 Redux Toolkit 的缺點:
- 繁瑣的模板代碼:
- 傳統的 Redux 需要手動定義大量的 action types 和 reducers,這可能會導致繁瑣的模板代碼。
- 較高的學習曲線:
- 尤其對於初學者來說,理解並正確使用 Redux 可能需要花費較長的時間。
總的來說,選擇是否使用 Redux Toolkit 取決於你的項目需求、開發團隊的經驗水準以及個人喜好。對於大多數情況來說,Redux Toolkit 提供了一個更現代化、簡潔且簡單上手的方式來使用 Redux。
接下來討論的是,如果專案內需要多個reducer的情況
雖然initialState中可以放很多種屬性,但是不同類型的邏輯放一起仍然會造成混亂
直接請ChatGPT示範一下!給個範例!
在一個專案中使用兩個處理不同邏輯的reducer,你可以透過 Redux Toolkit 的
createSlice
來創建兩個不同的Slice,每個Slice管理自己的狀態和相應的reducers。以下是一個實際範例,假設我們有一個專案,需要管理用戶的資訊和TODO列表:
首先,創建一個名為
userSlice.js
的檔案:import { createSlice } from '@reduxjs/toolkit'; const userSlice = createSlice({ name: 'user', initialState: { userInfo: null, }, reducers: { setUserInfo: (state, action) => { state.userInfo = action.payload; }, }, }); export const { setUserInfo } = userSlice.actions; export default userSlice.reducer;
這裡我們創建了一個名為
user
的Slice,它包含了一個 reducersetUserInfo
來設定用戶的資訊。接著,創建一個名為
todoSlice.js
的檔案:import { createSlice } from '@reduxjs/toolkit'; const todoSlice = createSlice({ name: 'todos', initialState: { list: [], }, reducers: { addTodo: (state, action) => { state.list.push({ id: Date.now(), text: action.payload, completed: false }); }, toggleTodo: (state, action) => { const todo = state.list.find(item => item.id === action.payload); if (todo) { todo.completed = !todo.completed; } }, removeTodo: (state, action) => { state.list = state.list.filter(item => item.id !== action.payload); }, }, }); export const { addTodo, toggleTodo, removeTodo } = todoSlice.actions; export default todoSlice.reducer;
這裡我們創建了一個名為
todos
的Slice,它包含了三個 reducers 來管理TODO列表。最後,在你的應用中使用這兩個Slice:
// store.js import { configureStore } from '@reduxjs/toolkit'; import userReducer from './userSlice'; import todoReducer from './todoSlice'; const store = configureStore({ reducer: { user: userReducer, todos: todoReducer, }, }); export default store;
這樣,你就在你的專案中使用了兩個處理不同邏輯的reducer,分別為
user
和todos
。每個Slice管理自己的狀態和相應的reducers,使得代碼更清晰、組織更有結構。
嗯嗯,透過configureStore,輕鬆將兩個reducer結合
但還沒有看到使用範例,建立是建立好了,實際在Component使用的樣子
請ChatGPT繼續生成範例
當然,我們可以繼續擴展這個範例,並在實際應用中使用這兩個Slice。
假設你有一個React應用,以下是如何在組件中使用這兩個Slice的範例:
1. 使用
userSlice
:// UserComponent.js import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { setUserInfo } from './userSlice'; function UserComponent() { const userInfo = useSelector((state) => state.user.userInfo); const dispatch = useDispatch(); const handleSetUserInfo = () => { dispatch(setUserInfo({ name: 'John Doe', email: 'john@example.com' })); }; return ( <div> <h2>User Info</h2> <p>Name: {userInfo?.name}</p> <p>Email: {userInfo?.email}</p> <button onClick={handleSetUserInfo}>Set User Info</button> </div> ); } export default UserComponent;
在這個範例中,我們使用
useSelector
從 Redux Store 中獲取userInfo
,並使用useDispatch
獲取 dispatch 函式。然後我們定義了一個handleSetUserInfo
函式,它會調用dispatch(setUserInfo())
來更新用戶信息。2. 使用
todoSlice
:// TodoComponent.js import React, { useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { addTodo, toggleTodo, removeTodo } from './todoSlice'; function TodoComponent() { const todos = useSelector(state => state.todos.list); const dispatch = useDispatch(); const [newTodo, setNewTodo] = useState(''); const handleAddTodo = () => { if (newTodo.trim() !== '') { dispatch(addTodo(newTodo)); setNewTodo(''); } }; return ( <div> <h2>TODO List</h2> <ul> {todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => dispatch(toggleTodo(todo.id))} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> <button onClick={() => dispatch(removeTodo(todo.id))}>Remove</button> </li> ))} </ul> <div> <input type="text" value={newTodo} onChange={(e) => setNewTodo(e.target.value)} /> <button onClick={handleAddTodo}>Add</button> </div> </div> ); } export default TodoComponent;
在這個範例中,我們使用了
useSelector
從 Redux Store 中獲取 TODO 列表,並使用useDispatch
獲取 dispatch 函式。然後我們定義了一個handleAddTodo
函式,它會調用dispatch(addTodo())
來新增一個 TODO。最後,在你的應用中,你可以將
UserComponent
和TodoComponent
集成到你的應用中,透過 Redux Store 來管理用戶信息和 TODO 列表。這樣,你就在你的應用中成功使用了兩個處理不同邏輯的reducer,分別為
user
和todos
。這種組織方式可以使你的代碼更有結構和清晰。
在以上對話中,我們探討了使用Redux Toolkit的
createSlice
來管理狀態的方法,並提供了以下重點:
- Redux Toolkit和createSlice介紹:
- Redux Toolkit是Redux官方提供的一個工具集,旨在簡化Redux的使用和開發流程。
createSlice
是其中的一個函式,用於創建包含了reducers和action的Slice,使得代碼更為簡潔和直觀。- createSlice的基本結構:
- 在創建Slice時,我們需要提供
name
(Slice的名稱)、initialState
(初始狀態)以及reducers
(用於處理不同action的函式)。- reducers的使用:
- reducers可以直接更動state的值,這是因為Redux Toolkit整合了Immer庫,使得在reducers中看似直接修改state的操作實際上是通過生成新的immutable版本來實現的。
- 接受傳入參數的reducers:
- 可以通過在action creator中返回一個函式,來實現reducers接受傳入參數的功能,並使用
action.payload
來獲取傳入的參數。- 在一個專案中使用多個reducers:
- 可以透過創建不同的Slice來管理不同部分的狀態和相應的reducers,然後在Redux Store中整合這些Slice。
總結來說,Redux Toolkit和
createSlice
提供了一個簡潔、現代化的方式來管理應用的狀態,同時保持了Redux的核心原則。這種方式使得開發者可以更專注於業務邏輯,同時減少了繁瑣的模板代碼。如果你是一名初學者,或者希望提升開發效率,我強烈推薦使用Redux Toolkit來進行狀態管理。希望這次的討論對你有所幫助,如果你有任何進一步的問題,請隨時告訴我。祝你在前端開發的道路上順利前行
本次對話紀錄:
https://chat.openai.com/share/787caf62-b90c-4228-a55d-f75b717532aa
其實我自己之前在學習Redux的時候,沒有一起學到Redux Toolkit的使用
導致我現在的專案裡面都是手動關聯起來的reducer、action creator等等
在這次的鐵人賽中,詢問ChatGPT相關議題,才發現喔喔喔有Redux Toolkit
而且可以把很多程式碼簡化
在不需要那麼高彈性的時候,藉由Redux Toolkit 的幫助
可以讓redux寫起來不要那麼複雜
最後,在本篇以及上一篇中,在reducer中都沒有和API的溝通
因為reducer必須沒有副作用、必須是純函數
不能讓request拿到的資料干擾處理結果
下一篇,要討論如何在redux中處理API結果! redux-thunk!
我是感謝ChatGPT教我認識Redux Toolkit 的前端工程師一宵三筵,我們明天見~