安裝:
官網安裝
此範例來自 Bruce 前端課程,使用 todoList 來練習
- 建立 store 同時,撰寫 reducer(會自動產生出對應 reducer 的 action)
- action 如同 dispatch 所送出的包裹
- reducer 會去拆解包裹,以了解用戶想做的事
應用於 todo-list
- store 中會存有幾條備忘錄,幾條 todo視像要做
- 建立資料夾 src / slice ,此 slice 中包含 reducer, action
TS-設立tool-kit
建立 store 的方式,要先建立 reducer
src / slice / todo.ts
- 建立 slice,並注意要傳入三個參數
- name : slice 的名字
- initialState 初始化的 state,建立初始狀態得值以及型別
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import { createSlice } from '@reduxjs/toolkit'
interface todoState { todoList: Array<string> } const initialState: todoState = { todoList: [] } export const todoSlice = createSlice({ name:'todo', initialState, reducers:{ addTodo: (state,action)=>{ state.todoList.push(action.payload) }, addTimestamp:(state)=>{ state.todoList.push(Date.now().toString()) } } } })
|
在此範例中會有兩個 dispatch
- submit
- record timestamp
- dispatch 會發出上面兩個 action
- reducer 要兩個事件接收
最後的 export todoSlice
- todoSlice 中會包含 actions , reducer , 以及其他 API
建立 store
- 位置 src / store.ts
1
| import { configureStore } from '@reduxjs/toolkit'
|
從上面過程,可以看到我們 store, action , reducer 都撰寫完成
使用
- 回到 UI 觸發狀態改變的地方
- index.tsx 裝入 store
- 像是 context 的概念作為 context provider 的內容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { Provider } from "react-redux"; import store from './store' ReactDOM.render(
<React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>, document.getElementById('root')
);
|
拿到 store 中的 todoList 來使用渲染於畫面
- 要如何拿到 store ? 使用 useSelector
- 在這裡要注意因為使用 TS 所以會有類型定義問題!
- 型別首先要來自於 store 檔案
- ReturnType: 幫我們將 store 中的內容,直接導出對應的 type
export type RootState = ReturnType<typeof store.getState>
- 回到 hooks 建立定義好型別的 API
1
| export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
- 可以 到 App 檔案或相關的子組件都可以調用 store 的內容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import { useAppSelector } from './hooks'; function App() {
const todoReducer = useAppSelector(state => state.todoReducer)
const todoList = todoReducer.todoList
return (
<Wrapper>
<Title>TODO LIST</Title> <NoteInput type="text" /> <SubmitBtn> Submit </SubmitBtn> <SubmitBtn> Record Timestamp </SubmitBtn> <Title>List</Title> { todoList.map((todo, index) => { return <Item key={todo}> <b>{index + 1}</b> {todo} </Item> }) }
</Wrapper>
);
}
export default App;
|
狀態更新
- 使用 dispatch 一些 action
- 定義 useDispatch
- hooks
1 2 3 4 5 6 7 8 9 10 11 12
| import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
- store.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { configureStore } from '@reduxjs/toolkit'
import todoReducer from './slice/todo'
const store = configureStore({
reducer: { todoReducer }
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export default store
|
到 App.tsx 使用 dispatch
- 宣告 dispatch
- 設置 setState 的值
- 綁定於 input 並讓 input onchange 時去觸發值得改變
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| import './App.css';
import styled from 'styled-components'
import { useAppSelector, useAppDispatch } from './hooks';
import { addTodo, addTimestamp } from './slice/todo'
import React, { useState } from 'react';
function App() {
const todoReducer = useAppSelector(state => state.todoReducer)
const todoList = todoReducer.todoList
const dispatch = useAppDispatch()
const [text, setText] = useState("")
return (
<Wrapper>
<Title>TODO LIST</Title> <NoteInput type="text" value={text} onChange={(e: React.ChangeEvent<HTMLInputElement>) => { setText(e.target.value) }} /> <SubmitBtn onClick={() => { if (text === '') { alert('請輸入TODO內容') return } dispatch(addTodo(text)) setText('') }}> Submit </SubmitBtn> <SubmitBtn onClick={() => { dispatch(addTimestamp()) }}> Record Timestamp </SubmitBtn> <Title>List</Title> { todoList.map((todo, index) => { return <Item key={todo}> <b>{index + 1}</b> {todo} </Item> }) }
</Wrapper>
);
}
export default App;
|
總結步驟
- 定義 slice 中的內容,其中包含 初始的資料定義、 reducers
- reducers 會透過 toolkit 這工具,將對應得 action 產生出來
- action 可以在 dispatch 做發送時帶出去
- 定義 provider 作為將 store 資料傳入的橋樑
- store 的產生:來自於將 slice 中的 reducers 建立好之後可以隨之建立的內容
- 建立 hooks :撰寫自定義的 hook ,將原本內建的 hook 進行重新包裝