嗨大家好,我是 Chris,今天將來說說如何在 React 中實現 Redux。
首先我們需要知道在 React 中,Redux 把 component 切割得非常清楚,分為專門處理 UI 和專門處理 State 的 component,也就是Presentational Component
和 Container Component
。
這樣就能更好的將 state logic 和一些需要重複使用的組件分得很清楚,如果有跟過這一系列的專案的話,我一直以來都盡量把 state 往頂層放( 希望你有注意到QQ ) 且用 props 一層層的傳下來,這樣一來就可以輕易地重複取用 component (像是 title、infoboard ),也能輕鬆的自訂自己需要的內容。
可就算是這樣還是有一些 State 參雜在 UI 介面之中,不說那個 Pure Component 自帶的效能 buff,有時候明明是差不多的套件卻會需要浪費時間在寫一次。
所以這樣的切割搭配 Redux 的 api component 將能大大的提升撰寫專案的效率。
註:當你看完以上的解說,或許會想到這跟 hook 有點像其實是沒有錯的,因為 hook api 就是解決了這些狀態管理之間的問題,詳細的資訊我曾經在 day-10 說過,歡迎去看看。
也就是純 UI 介面,不參雜任何 web request、state 處理,有的只是藉由 props 取用資料顯示畫面。
import React from "react";
import Title from "./Title";
export default function FeaturedBoard({ children, title,className }) {
return (
<div className="featuredboard">
<div className="coverboard">
<Title title={title} className={className}/>
{children}
</div>
</div>
);
}
像是這種可以重複使用的 component 就是一個很標準的 Presentational Component
如果有跟過前面的系列專案的話,絕大多數的 component 都是 Presentational Component。
只有少部分的 component ( Home、 Notes、NotesDetail等等只要有資料的都算,雖然說資料不應該在那啦。)
沒有跟過的話可以從 這裡 取得之前的進度。
註:我們在專案中有使用過幾個 package,所以下載下來一定要
npm install
安裝所有的 package,才能啟動。
只要有任何處理資料的 component,像是上面的 Home、 Notes、NotesDetail等等,就算只是呼叫 data 的也算, Reducer 、 Action Creator 這些處理 state 的當然也是。
我們通常會在這類 Component 裡藉由 subscribe、getState、dispatch 等 api function 處理 state。
// NotesDetail.js
// 接收 param
let { param } = useParams({});
// 設定 state
const [note, setNote] = useState([]);
//使用 filter 找到要的內容
useEffect(() => {
setNote(...Notes.filter((item) => item.title === { param }.param));
}, []);
return (
<div>
<FeaturedBoard title="Note Title" className="title">
<Banner article={article} />
</FeaturedBoard>
<InfoBoard pic={null}>
<section className="note">
<Title title={note.title} className="subtitle" />
<div className="note-datetime">by Chris {note.date}</div>
<div className="note-area">
<p className="note-chapter">chapter 1</p>
<article className="note-article">{note.article}</article>
<article className="note-article">{note.article}</article>
<p className="note-bold">粗體</p>
<p className="note-slash">斜體</p>
</div>
</section>
</InfoBoard>
</div>
);
}
像是 NotesDetail.js 這種本身自己擁有 state,且又呼叫 param 的 component 就是很明確的Container Component。
當你不可避免的讓你的 ui component 擁有且處理 state( 這是一定會發生的 ),這時我們就必須使用 Connect()
這個 React-Redux 特有的 function。
大家應該還記得我們昨天提到的 Action 吧,由於在 Redux 中傳遞的 state 都是 Action 這種普通 component 無法辨別的 object,所以需要 Connect()
讓 component 能夠讀取 Redux 的 state。
Connect()
接收兩個參數,mapStateToProps
和 mapDispatchToProps()
,如果我們想要讓這個 component 可以接收和處理 state,就會像這樣:
import { connect } from 'react-redux'
function Presentational(){
...
}
const Container = connect(
mapStateToProps,
mapDispatchToProps
)(Presentational)
export default Container
Presentational 就是我們原本的 UI component,Container 就是我們藉由 connect() 轉變為處理 state 的 Component 所定義的名稱。
如果想單獨把 UI Component 轉變為 Container Component 也可以只使用這樣:
const Container = connect()(Presentational)
雖然我不知道這可以幹嘛 XD,不過還是提一下,知道的大神們希望也不吝賜教。
簡單的講,就是將一開始存在 state tree 裡的初始資料讀出來。
有關 initial state tree 的來源,這邊可以簡單的說一下,從 Store 的第2個參數 preloadedState 接收預設的 state 或是 middleware 套一些 api 讓我們拿得到 web state。
還有最後一種最常見的,就是在設定 reducer 時的初始資料,在昨天說到 reducer 時有提到:
initialState:{
List:[]
}
我覺得這個是一個痛點,因為我一開始在搞懂 store 的概念時花了很多時間,想說我翻遍了整個 demo 文件都沒有看到定義 data 的地方 (國王的data),後來才發現,每一個 reducer 所定義的 initial state 集結而成就是 store 的 state tree,現在分享給大家,讓你們少走冤枉路QQ
好的回到正題,mapStateToProps 可以把放在 store 的 state 放進 component 可以讀取的 state,大概會像這樣:
const [article, setNote] = useState();
const [chpater, setNote] = useState();
const mapStateToProps = (state) => {
return {
article : state.note.article,
chpater : state.note.chpater,
...
}
}
這樣就可以使用我們熟悉的 state 了。
既然有了輸入端,也會有輸出端,當我們有希望透過 reducer 去改變存在 store 的 state 時,我們就可以在這裡使用 dispatch 去達成想做的事。
使用起來像是這樣:
const mapDispatchToProps = dispatch => {
return {
getNotes: () => dispatch(fetchNoteList()),
addNotes: (note) => dispatch(addNotes(note)),
deleteNotes: (index) => dispatch(NotesList.update(index))
};
};
這樣就可以像普通 function 一樣傳遞 props 和透過 onclick 或 handlechange 去觸發,當然,最後的資料結構還是 action ,傳進去的 props 也是要符合你所建立 action。
雖然我們建立了整個 redux 系統,那我們該把這層架構放在哪呢?
記不記得我在開頭說過的,把所有的 state 放在最頂層的架構,這將可以讓我們能夠最大限度的使用 Redux。
我們在 app.js 管理 router、在 app下面的各個 page 建構了 UI 架構,所以看起來最好的位置就是放在 index 裡。
這時就可以使用 Provider()
這個 React-Redux 提供的 function,幫助我們實作,看起來會像這樣:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import Provider from "react-redux";
import store from "./store/index";
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
這樣我們就可以在 React 裡使用 Redux 管理 state了。
以上就是 Redux 在 React 的作用方式,明天將更改我們的專案讓 Redux 接管資料流。