<style> html, body, .ui-content { background-color: #333; color: #ddd; } p { font-size:20px; } .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { color: #ddd; } .markdown-body h1, .markdown-body h2 { border-bottom-color: #ffffff69; } .markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link, .markdown-body h5 .octicon-link, .markdown-body h6 .octicon-link { color: #fff; } .markdown-body img { background-color: transparent; } .ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a { color: white; border-left: 2px solid white; } .expand-toggle:hover, .expand-toggle:focus, .back-to-top:hover, .back-to-top:focus, .go-to-bottom:hover, .go-to-bottom:focus { color: white; } .ui-toc-dropdown { background-color: #333; } .ui-toc-label.btn { background-color: #191919; color: white; } .ui-toc-dropdown .nav>li>a:focus, .ui-toc-dropdown .nav>li>a:hover { color: white; border-left: 1px solid white; } .markdown-body blockquote { color: #bcbcbc; } .markdown-body table tr { background-color: #5f5f5f; } .markdown-body table tr:nth-child(2n) { background-color: #4f4f4f; } .markdown-body code, .markdown-body tt { color: #eee; background-color: rgba(230, 230, 230, 0.36); } a, .open-files-container li.selected a { color: #5EB7E0; } .eng{ color : red; background-color:#e6e4e1; } </style> # Redux的介紹及實戰教學 ## 不負責任的說明:D 本教學主要教導最初學習<span class="eng">Redux</span >的幾個步驟,在許多的層面或許會有更好的寫法,但為了能夠讓初學者更快的了解內容及原理,本文章將以較為簡單的方式進行介紹。 ## 介紹目錄 :::info 本文章的介紹順序: - 為何要使用Redux - Redux的幾個名詞介紹 - Reducer - Action - Store - Provider - 實際運用流程 - 結語 ::: ## 為何要使用Redux 如果我們使用<span class="eng">React</span >時使用了眾多的<span class="eng">Components</span >並且這些<span class="eng">Components</span >都是來自一個資料時,那我們的資料傳遞寫出來後就會像下圖左方這樣雜亂不堪。 當下方的<span class="eng">Components</span >想要修改最上方傳過來的資料時,就需要將更改的Function從上層一層一層的傳過來,對於大型的專案非常的不友善且反人類的。 ![](https://i.imgur.com/bhmJbpi.png) 而當我們使用<span class="eng">Redux</span >之後呢就只需要將資料放在<span class="eng">Store</span >裡,而需要這些資料時只要跟<span class="eng">Store</span >要就好了! ## Redux的幾個名詞介紹 ## Reducer ![](https://i.imgur.com/uG8ebO3.png) 這是一個以加減作為範例的<span class="eng">Reducer</span >,剛學習時我們能夠將<span class="eng">Reducer</span >當成只能設置一個 <span class="eng">State</span > 的 <span class="eng">Components</span > 我們能夠在圖片標示 1 的地方看到 state=1 ,意思就是設定這個 <span class="eng">Reducer</span > 的值為 1 當然也能夠設置成物件,陣列等等。 而當其他元件呼叫這個元件的值時就會回傳State的值回去。 那我們有辦法去更改這個 <span class="eng">State</span > 嗎? 答案當然是可以的,當其他 <span class="eng">Components</span > 想要去更改這個 <span class="eng">Reducer</span > 的值時,他們會需要在呼叫這個 <span class="eng">Reducer</span > 的同時也透過 <span class="eng">action</span > 傳入一個 <span class="eng">action.type</span > 讓我們能夠在這個 <span class="eng">Reducer</span > 中使用<span class="eng">Switch</span >判斷他要進行的是哪一個<span class="eng">case</span >。 以範例來說,當有人呼叫 <span class="eng">Counter</span > 這個 <span class="eng">Reducer</span > 並且傳入 {action.type : INCREMENT} 的話,那就會將這個<span class="eng">Counter</span > 中的 <span class="eng">State</span > 也就是原本的 1 去進行 +1 那當其他元件再次呼叫 <span class="eng">Counter</span > 的值時就會變成 2 了喔!! 而當然我們也能夠帶入變數到這個<span class="eng">Dispatch</span>裡面,我們能夠在圖片標註 4 的地方看見,當我們傳入{action.type : INCREASE , action.value:???}時就能夠將<span class="eng">State</span>的值加上???了喔!! ## Action 在上一個名詞<span class="eng">Reducer</span >中我們知道了需要更改<span class="eng">Reducer</span >時我們需要透過傳入一個<span class="eng">action.type</span >來讓<span class="eng">Reducer</span >知道我們想要觸發的是哪一邊的<span class="eng">case</span>,那麼究竟要如何進行傳入呢? 這時候我們就需要另一個函式<span class="eng">action</span>來做這件事了。 舉個例子,一般當我們要在元件呼叫上個例子將<span class="eng">State</span>+1的事情時,我們需要使用到<span class="eng">Dispatch</span>這個方式並在需要呼叫的地方這麼打。 ```javascript= dispatch( {type: "INCREMENT"} ); ``` 看起來還好嗎? 那如果我們使用的是需要傳入變數得函式呢? 那我們就需要這樣 ```javascript= dispatch( {type: "INCREASE",變數命名:變數} ); ``` 看起來變得複雜許多呢,那我們有沒有辦法讓能夠讓我們不需要再<span class="eng">Dispatch</span>中打入這麼多東西呢? 答案當然是可以的。 我們另外建立一個js檔案,並依照範例撰寫之後,就能夠看出差異了喔! ![](https://i.imgur.com/TNsqwjP.png) 從原本的 ```javascript= dispatch( {type: "INCREASE",變數命名:變數} ); ``` To ```javascript= dispatch( increase(變數) ); ``` 是不是變得很簡單了呢? ## store 一個專案中可能會有多個<span class="eng">Dispatch</span>所以我們會需要一個東西來進行匯集,而這個東西就是<span class="eng">Store</span>,創建<span class="eng">Store</span>的指令如下 ```javascript= const store = configureStore({ reducer: allReducers }); ``` 但如果我們有很多的<span class="eng">Reducer</span>呢?譬如我有一個加減計算的<span class="eng">Reducer</span>和一個代辦清單的<span class="eng">Reducer</span>的話要怎麼匯入呢? 這個時候我們就需要用到<span class="eng">combineReducers</span> <span class="eng">Reducers</span>匯入進一個檔案後呢,並使用<span class="eng">combineReducers</span>這個函式把他們整合成一個<span class="eng">Reducer</span>。 範例如下: ![](https://i.imgur.com/zggsl68.png) 我們能夠在最上方匯入自己得<span class="eng">Dispatch</span>並且能夠在裡面取名字。而取的名字也影響到如何取得這個<span class="eng">Reducer</span>的值喔! 例如我在第6行為我的<span class="eng">CounterReducer</span>取名叫做<span class="eng">counter</span>那當其他元件想要取得這個<span class="eng">Reducer</span>的值時就需要這樣呼叫喔: ```javascript= const ??? = useSelector(state => state.counter); ``` ## Provider 而當我們創建好<span class="eng">Store</span>之後,就需要透過<span class="eng">Provider</span>將這個<span class="eng">Store</span>餵給我們最上層的父元件<span class="eng">App</span>這樣才能讓底下的元件都抓的到<span class="eng">Store</span>喔!! 範例如下: ![](https://i.imgur.com/K89TIcc.png) ## 實際運用流程 終於講完露露燈的名詞介紹了,但在開始之前還有一件事情必須做! 那就是我們可以先下載Redux的Debug擴充元件,能夠更好的協助我們觀察變數喔! 首先我們先至這裡[進行](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=zh-TW)進行下載。 (怕我亂藏連結的同學可以自行Google Redux devtool喔!) 下載完之後等等在專案中就能夠使用到了! ### 事前準備 那麼在這個範例中我們先以簡單的加減做為例子。 首先我們先下載<span class="eng">Redux</span>以及他的工具箱 ```javascript= npm install react-redux npm install @reduxjs/toolkit ``` 接著我們在src資料夾下建立actions以及reducers用來存放這些東西。 那我們接著就來撰寫第一支<span class="eng">Reducer</span>吧! 我們在reducers資料夾下新增Counter.js 並且撰寫如下 ```javascript= const Counter = (state = 1, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; case 'INCREASE': const newState = state + Number(action.value); return newState; default: return state } } export default Counter; ``` 而後一樣在reducers資料夾下新增index.js用來使用<span class="eng">combineReducers</span> 撰寫如下: ```javascript= import CounterReducer from "./Counter"; import { combineReducers } from "redux"; const allReducers = combineReducers({ counter:CounterReducer, }) export default allReducers; ``` 我們就能夠將取得的<span class="eng">allReducers</span>拿去餵給<span class="eng">Store</span>了! 但在此之前我們記得也要先在actions中建立一個index.js用以存放我們的action,這樣之後才能夠方便呼叫喔! 撰寫如下: ```javascript= export const increment = () =>{ return{ type: 'INCREMENT', }; } export const decrement = () =>{ return{ type: 'DECREMENT', }; } export const increase = (value) =>{ return{ type: 'INCREASE', value:value, }; } ``` 接下來我們到原先在跟目錄中的index.js,引入會使用的套件庫及變數,並將元件App使用<span class="eng">Provider</span>進行包覆,從而讓以下元件取得的到<span class="eng">Store</span>。 撰寫如下: ```javascript= import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import reportWebVitals from './reportWebVitals'; import { configureStore } from "@reduxjs/toolkit"; import allReducers from "./reducers"; import { Provider } from "react-redux"; const store = configureStore({ reducer: allReducers }); const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode> ); // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals reportWebVitals(); ``` 接下來我們使用 ```javascript= npm start ``` 來開啟我們的專案,接著F12打開檢查。 應該會在最右邊發現我們的剛剛下載的Redux devtool在這邊。 ![](https://i.imgur.com/pvkqmzU.png) 接著點擊畫面中的State,應該就能夠看見我們剛剛創立的Counter了! ![](https://i.imgur.com/jongh7n.png) ### 取出State 那我們如何在元件中取得值呢? 我們到專案給予我們的App.js中。 將能夠取得<span class="eng">State</span>的函式匯入: ```javascript= import { useSelector } from "react-redux"; ``` 使用函式將變數取出: ```javascript= const counter = useSelector(state=>state.counter); ``` 最後將畫面渲染: ```javascript= <div className="App"> Counter from Store is :{counter}; </div> ``` 接著存檔就能夠在畫面中看到了喔! ![](https://i.imgur.com/orbJQiH.png) 所有在App.js下的程式碼: ```javascript= import './App.css'; import { useSelector } from "react-redux"; function App() { const counter = useSelector(state=>state.counter); return ( <div className="App"> Counter from Store is :{counter}; </div> ); } export default App; ``` ### 更改State 我們將能夠更改<span class="eng">State</span>會使用到的函式匯入,並將之命名,以便我們能夠在onClick事件中使用: ```javascript= import { useDispatch } from "react-redux"; const dispatch = useDispatch(); ``` 並且在匯入我們存放在actions資料夾下的index.js中的幾個函式來運用。 ```javascript= import { increment, decrement, increase} from "./actions"; ``` 並且新增兩個Button讓我們能夠操作。 並在其中新增onClick事件能夠使用<span class="eng">Dispatch</span>來使用相對應的<span class="eng">action</span>,撰寫如下: ```javascript= return ( <div className="App"> Counter from Store is :{counter}; <button onClick={()=>dispatch(increment())}>增加</button> <button onClick={()=>dispatch(decrement())}>減少</button> </div> ); ``` 之後在網頁中就能夠透過點擊來增加或減少<span class="eng">State中的Counter</span>喽! ## 結語 感謝各位看到這邊,這篇文章主要是希望能夠透過非常非常簡單的方式去讓大家學會操作<span class="eng">Redux</span>因此文章的步調非常的緩慢,不過老鳥應該也不會查到這一篇文章了所以應該沒有問題吧(汗。 而文章中也有提到能夠透過傳變數的方式區更改<span class="eng">State</span>的值,而這就是我留給大家的功課了,希望大家都能夠完成喔! 那這一次的教學就到這邊,我是Agry,我們下次見~