我們研發開源了一款基於 Git 進行技術實戰教程寫作的工具,我們圖雀社群的所有教程都是用這款工具寫作而成,歡迎 Star 哦
如果你想快速瞭解如何使用,歡迎閱讀我們的 教程文件哦
歡迎繼續閱讀《Taro 小程式開發大型實戰》系列,前情回顧:
- 熟悉的 React,熟悉的 Hooks:我們用 React 和 Hooks 實現了一個非常簡單的新增帖子的原型
- 多頁面跳轉和 Taro UI 元件庫:我們用 Taro 自帶的路由功能實現了多頁面跳轉,並用 Taro UI 元件庫升級了應用介面
- 實現微信和支付寶多端登入:實現了微信、支付寶以及普通登入和退出登入
- 使用 Hooks 版的 Redux 實現大型應用狀態管理(上篇):使用 Hooks 版的 Redux 實現了
user
邏輯的狀態管理重構
這是使用 Hooks 版的 Redux 重構狀態管理的下篇,在上篇中我們實現了 user
部分 的狀態管理的重構,但受限於篇幅,我們還剩下 Footer
元件部分沒有重構,在這一篇中,我們將首先實現 Footer
元件的狀態管理的重構,接著我們馬上來實現 post
邏輯的狀態管理的重構。
如果你不熟悉 Redux,推薦閱讀我們的《Redux 包教包會》系列教程:
本文所涉及的原始碼都放在了 Github 上,如果您覺得我們寫得還不錯,希望您能給❤️這篇文章點贊+Github倉庫加星❤️哦~
搞定 Footer 的 Redux 化
本來這個小標題我是不想起的,但是因為,是吧,大家上面在沒有小標題的情況下看了這麼久,可能已經廢(累)了,所以我就貼心的加上一個小標題,幫助你定位接下來講解的重心。
是的接下來,我們要重構 “我的” tab 頁面中的下半部分元件 src/components/Footer/index.js
我們遵循自頂向下的方式來重構,首先是 src/components/Logout/index.js
檔案,我們開啟這個檔案,對其中內容作出如下修改:
import Taro, { useState } from '@tarojs/taro'
import { AtButton } from 'taro-ui'
import { useDispatch } from '@tarojs/redux'
import { SET_LOGIN_INFO } from '../../constants'
export default function LoginButton(props) {
const [isLogout, setIsLogout] = useState(false)
const dispatch = useDispatch()
async function handleLogout() {
setIsLogout(true)
try {
await Taro.removeStorage({ key: 'userInfo' })
dispatch({
type: SET_LOGIN_INFO,
payload: {
avatar: '',
nickName: '',
},
})
} catch (err) {
console.log('removeStorage ERR: ', err)
}
setIsLogout(false)
}
return (
<AtButton type="secondary" full loading={isLogout} onClick={handleLogout}>
退出登入
</AtButton>
)
}
這一步可能是最能體現引入 Redux 進行狀態管理帶來好處的一步了 – 我們將之前至上而下的 React 狀態管理邏輯壓平,使得底層元件可以在自身中就解決響應的狀態和邏輯問題。
可以看到,我們上面的檔案中主要有五處改動:
- 首先我們從
@tarojs/taro
裡面匯出useState
Hooks。 - 接著我們將之前在
src/pages/mine/mine.js
中定義的isLogout
狀態移動到元件Logout
元件內部來,因為它只和此元件有關係。 - 接著我們用
isLogout
替換在AtButton
裡面用到的props.loading
屬性。 - 然後,我們考慮將之前按鈕點選呼叫
props.handleLogout
Redux 化,我們將這個點選之後的回撥函式handleLogout
在元件內部定義。 - 最後,我們從
@tarojs/redux
中匯入useDispatch
Hooks,並在元件中呼叫成我們需要的dispatch
函式,接著我們在handleLogout
函式中去 dispatch 一個SET_LOGIN_INFO
action 來重置 Store 中的nickName
和avatar
屬性。
提示
這裡我們在元件內定義的
handleLogout
函式和我們之前在src/pages/mine/mine.js
中定義的類似,只是使用 dispatch action 的方式替換了重置nickName
和avatar
的部分。
搞定完 Logout
元件,接著就是 LoginForm
元件的重構了,讓我們快馬加鞭,讓它也接受 Redux 光環的洗禮吧!
開啟 src/components/LoginForm/index.jsx
,對其中的內容作出相應的修改如下:
import Taro, { useState } from '@tarojs/taro'
import { View, Form } from '@tarojs/components'
import { AtButton, AtImagePicker } from 'taro-ui'
import { useDispatch } from '@tarojs/redux'
import { SET_LOGIN_INFO, SET_IS_OPENED } from '../../constants'
import './index.scss'
export default function LoginForm(props) {
// Login Form 登入資料
const [formNickName, setFormNickName] = useState('')
const [files, setFiles] = useState([])
const [showAddBtn, setShowAddBtn] = useState(true)
const dispatch = useDispatch()
function onChange(files) {
if (files.length > 0) {
setShowAddBtn(false)
} else {
setShowAddBtn(true)
}
setFiles(files)
}
function onImageClick() {
Taro.previewImage({
urls: [props.files[0].url],
})
}
async function handleSubmit(e) {
e.preventDefault()
// 鑑權資料
if (!formNickName || !files.length) {
Taro.atMessage({
type: 'error',
message: '您還有內容沒有填寫!',
})
return
}
setShowAddBtn(true)
// 提示登入成功
Taro.atMessage({
type: 'success',
message: '恭喜您,登入成功!',
})
// 快取在 storage 裡面
const userInfo = { avatar: files[0].url, nickName: formNickName }
// 清空表單狀態
setFiles([])
setFormNickName('')
// 快取在 storage 裡面
await Taro.setStorage({ key: 'userInfo', data: userInfo })
dispatch({ type: SET_LOGIN_INFO, payload: userInfo })
// 關閉彈出層
dispatch({ type: SET_IS_OPENED, payload: { isOpened: false } })
}
return (
<View className="post-form">
<Form onSubmit={handleSubmit}>
<View className="login-box">
<View className="avatar-selector">
<AtImagePicker
length={1}
mode="scaleToFill"
count={1}
files={files}
showAddBtn={showAddBtn}
onImageClick={onImageClick}
onChange={onChange}
/>
</View>
<Input
className="input-nickName"
type="text"
placeholder="點選輸入暱稱"
value={formNickName}
onInput={e => setFormNickName(e.target.value)}
/>
<AtButton formType="submit" type="primary">
登入
</AtButton>
</View>
</Form>
</View>
)
}
這一步和上一步類似,可能也是最能體現引入 Redux 進行狀態管理帶來好處的一步了,我們同樣將之前在頂層元件中提供的狀態壓平到了底層元件內部。
可以看到,我們上面的檔案中主要有四處改動:
- 首先我們將
formNickName
和files
等狀態放置到LoginForm
元件內部,並使用useState
Hooks 管理起來,因為它們只和此元件有關係。 - 接著,我們將
AtImagePicker
裡面的props.files
替換成files
,將它的onChange
回撥函式內部的設定改變狀態的props.handleFilesSelect(files)
替換成setFiles(files)
。可以看到這裡我們還對files.length = 0
的形式做了一個判斷,當沒有選擇圖片時,要把我們選擇圖片的按鈕顯示出來。 - 接著,我們將
Input
元件的props.formNickName
替換成formNickName
,將之前onInput
接收的回撥函式換成了setFormNickName
的形式來設定formNickName
的變化。 - 接著,我們將之前提交表單需要呼叫的父元件方法
props.handleSubmit
移動到元件內部來定義,可以看到,這個hanldeSubmit
組合了之前在src/components/Footer/index.jsx
和src/pages/mine/mine.js
元件裡的handleSubmit
邏輯:- 首先使用
e.preventDefault
禁止瀏覽器預設行為。 - 接著進行資料驗證,不合要求的資料就會被駁回並顯示錯誤(其實這裡應該顯示警告
warning
,當時寫程式碼時石樂志?)。 - 接著因為
LoginForm
表單資料要被清除,所以我們將選中圖片的按鈕又設定為可顯示狀態。 - 接著提示登入成功。
- 清空表單狀態。
- 將登入資料快取在
storage
裡面,在 Taro 裡面使用Taro.setStorage({ key, data })
的形式來快取,其中key
是字串,data
是字串或者物件。- 最後我們匯出了
useDispatch
Hooks,使用useDispatch
Hooks 生成的dispatch
函式的引用來發起更新 Redux store 的 action 來更新本地資料,type
為SET_LOGIN_INFO
的 action 用來更新使用者登入資訊,type
為SET_IS_OPENED
的 action 用來更新isOpened
屬性,它將關閉展示登入框的彈出層FloatLayout
元件。
- 最後我們匯出了
- 首先使用
講到這裡,我們的 Footer
部分的重構大業還剩下臨門一腳了。讓我們開啟 src/components/Footer/index.js
檔案,立馬來重構它:
import Taro from '@tarojs/taro'
import { View } from '@tarojs/components'
import { AtFloatLayout } from 'taro-ui'
import { useSelector, useDispatch } from '@tarojs/redux'
import Logout from '../Logout'
import LoginForm from '../LoginForm'
import './index.scss'
import { SET_IS_OPENED } from '../../constants'
export default function Footer(props) {
const nickName = useSelector(state => state.user.nickName)
const dispatch = useDispatch()
// 雙取反來構造字串對應的布林值,用於標誌此時是否使用者已經登入
const isLogged = !!nickName
// 使用 useSelector Hooks 獲取 Redux Store 資料
const isOpened = useSelector(state => state.user.isOpened)
return (
<View className="mine-footer">
{isLogged && <Logout />}
<View className="tuture-motto">
{isLogged ? 'From 圖雀社群 with Love ❤' : '您還未登入'}
</View>
<AtFloatLayout
isOpened={isOpened}
title="登入"
onClose={() =>
dispatch({ type: SET_IS_OPENED, payload: { isOpened: false } })
}
>
<LoginForm />
</AtFloatLayout>
</View>
)
}
可以看到上面的程式碼主要有五處改動:
- 首先我們已經將
nickName
抽取到 Redux store 儲存的狀態中,所以之前從父元件獲取的props.isLogged
判斷是否登入的資訊,我們移動到元件內部來,使用useSelector
Hooks 從 Redux store 從獲取nickName
屬性,進行雙取反操作成布林值來表示是否已經登入的isLogged
屬性,並使用它來替換之前的props.isLogged
屬性。 - 接著,就是取代之前從父元件獲取的
props.isOpened
屬性,我們使用useSelector
Hooks 從 Redux store 中獲取對應的isOpened
屬性,然後替換之前的props.isOpened
,使用者控制登入框視窗的彈出層AtFloatLayout
的開啟和關閉。 - 接著,我們將之前
AtFloatLayout
關閉時(onClose
)的回撥函式替換成 dispatch 一個type
為SET_IS_OPENED
的 action 來設定isOpened
屬性將AtFloatLayout
關閉。 - 接著,我們開始移除
Logout
和LoginForm
元件上不再需要傳遞的屬性,因為在對應的元件中我們已經宣告瞭對應的屬性了。 - 最後,我們刪掉之前定義在
Footer
元件內的formNickName
和files
等狀態,以及不再需要的handleSubmit
函式,因為它已經在LoginForm
裡面定義了。
完成 “我的” 頁面重構
熟悉套路的同學可能都知道起這個標題的含義了吧 ?。
我們一路打怪重構到這裡,相比眼尖的人已經摸清楚 Redux 的套路了,結合 Redux 來寫 React 程式碼,就好比 “千里之堤,始於壘土” 一般,我們先把所有細小的分支元件搞定,進而一步一步向頂層元件進發,以完成所有元件的編寫。
而這個 src/pages/mine/mine.jsx
元件就是 “我的” 這一 tab 頁面的頂層元件了,也是我們在 “我的” 頁面需要重構的最後一個頁面了,是的,我們馬上就要達到第一階段性勝利了✌️。現在就開啟這個檔案,對其中的內容作出如下的修改:
import Taro, { useEffect } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { useDispatch } from '@tarojs/redux'
import { Header, Footer } from '../../components'
import './mine.scss'
import { SET_LOGIN_INFO } from '../../constants'
export default function Mine() {
const dispatch = useDispatch()
useEffect(() => {
async function getStorage() {
try {
const { data } = await Taro.getStorage({ key: 'userInfo' })
const { nickName, avatar } = data
// 更新 Redux Store 資料
dispatch({ type: SET_LOGIN_INFO, payload: { nickName, avatar } })
} catch (err) {
console.log('getStorage ERR: ', err)
}
}
getStorage()
})
return (
<View className="mine">
<Header />
<Footer />
</View>
)
}
Mine.config = {
navigationBarTitleText: '我的',
}
可以看到,上面的程式碼做了一下五處改動:
- 我們匯入了
useDispatch
Hooks 和SET_LOGIN_INFO
常量,並把之前在getStorage
方法裡面設定nickName
和avatar
的操作替換成了 dispatch 一個type
為SET_LOGIN_INFO
的 action。 - 接著我們刪除不再需要的
formNickName
、files
、isLogout
、isOpened
狀態,以及setLoginInfo
、handleLogout
、handleSetIsOpened
、handleClick
、handleSubmit
方法。 - 最後我們刪除
Header
和Footer
元件上不再不需要的屬性。
大功告成?!這裡給你頒發一個銀牌,以獎勵你能一直堅持閱讀並跟到這裡,我們這一篇教程很長很長,能跟下來的都不容易,希望你能在心裡或用實際行動給自己鼓鼓掌?。
小憩一下,恢復精力,整裝待發!很多同學可能很好奇了,為什麼還只能拿一個銀牌呢?那是因為我們的重構程式才走了一半呀✌️,但是不要擔心,我們所有新的東西都已經講完了,接下來就只是一些收尾工作了,當你能堅持到終點的時候,會有驚喜等著你哦!加油吧騷年?。
開始重構 “首頁” 之旅
我們依然按照之前的套路,從最底層的元件開始重構,首先是我們的登入框彈出層 LoginForm
元件,讓我們開啟 src/components/PostForm/index.jsx
檔案,對其中的內容作出相應的修改如下:
import Taro, { useState } from '@tarojs/taro'
import { View, Form, Input, Textarea } from '@tarojs/components'
import { AtButton } from 'taro-ui'
import { useDispatch, useSelector } from '@tarojs/redux'
import './index.scss'
import { SET_POSTS, SET_POST_FORM_IS_OPENED } from '../../constants'
export default function PostForm(props) {
const [formTitle, setFormTitle] = useState('')
const [formContent, setFormContent] = useState('')
const nickName = useSelector(state => state.user.nickName)
const avatar = useSelector(state => state.user.avatar)
const dispatch = useDispatch()
async function handleSubmit(e) {
e.preventDefault()
if (!formTitle || !formContent) {
Taro.atMessage({
message: '您還有內容沒有填寫完哦',
type: 'warning',
})
return
}
dispatch({
type: SET_POSTS,
payload: {
post: {
title: formTitle,
content: formContent,
user: { nickName, avatar },
},
},
})
setFormTitle('')
setFormContent('')
dispatch({
type: SET_POST_FORM_IS_OPENED,
payload: { isOpened: false },
})
Taro.atMessage({
message: '發表文章成功',
type: 'success',
})
}
return (
<View className="post-form">
<Form onSubmit={handleSubmit}>
<View>
<View className="form-hint">標題</View>
<Input
className="input-title"
type="text"
placeholder="點選輸入標題"
value={formTitle}
onInput={e => setFormTitle(e.target.value)}
/>
<View className="form-hint">正文</View>
<Textarea
placeholder="點選輸入正文"
className="input-content"
value={formContent}
onInput={e => setFormContent(e.target.value)}
/>
<AtButton formType="submit" type="primary">
提交
</AtButton>
</View>
</Form>
</View>
)
}
這個檔案的形式和我們之前的 src/components/LoginForm/index.jsx
檔案類似,可以看到,我們上面的檔案中主要有四處改動:
- 首先我們將
formTitle
和formContent
等狀態放置到PostForm
元件內部,並使用useState
Hooks 管理起來,因為它們只和此元件有關係。 - 接著,我們將
Input
裡面的props.formTitle
替換成formTitle
,將它的onInput
回撥函式內部的設定改變狀態的props. handleTitleInput
替換成setFormTitle(e.target.value)
的回撥函式。 - 接著,我們將
Textarea
元件的props. formContent
替換成formContent
,將之前onInput
接收的回撥函式換成了setFormContent
的形式來設定formContent
的變化。 - 最後,我們將之前提交表單需要呼叫的父元件方法
props.handleSubmit
移動到元件內部來定義,可以看到,這個hanldeSubmit
和我們之前定義在src/pages/index/index.js
元件裡的handleSubmit
邏輯類似:- 首先使用
e.preventDefault
禁止瀏覽器預設行為。 - 接著進行資料驗證,不合要求的資料就會被駁回並顯示警告(這裡我們又顯示對了?)。
- 接著 dispatch 一個
type
為SET_POSTS
的 action,將新發表的 post 新增到 Redux store 對應的posts
陣列中。我們注意到這裡我們使用useSelector
Hooks 從 Redux store 裡面獲取了nickName
和avatar
屬性,並把它們組合到post.user
屬性裡,隨著 action 的 payload 一起被 dispatch,我們用這個user
屬性標誌發帖的使用者屬性。 - 清空表單狀態。
- 接著我們 dispatch 一個
type
為SET_POST_FORM_IS_OPENED
的 action 用來更新isOpened
屬性,它將關閉展示發表帖子的表單彈出層FloatLayout
元件。 - 最後提示發帖成功。
- 首先使用
接著是我們 “首頁” 頁面元件另外一個底層子元件 PostCard
,它主要用於展示一個帖子,讓我們 src/components/PostCard/index.jsx
檔案,對其中的內容作出對應的修改如下:
import Taro from '@tarojs/taro'
import { View } from '@tarojs/components'
import classNames from 'classnames'
import { AtAvatar } from 'taro-ui'
import './index.scss'
export default function PostCard(props) {
// 注意:
const { title = '', content = '', user } = props.post
const { avatar, nickName } = user || {}
const handleClick = () => {
// 如果是列表,那麼就響應點選事件,跳轉到帖子詳情
if (props.isList) {
Taro.navigateTo({
url: `/pages/post/post?postId=${props.postId}`,
})
}
}
const slicedContent =
props.isList && content.length > 66
? `${content.slice(0, 66)} ...`
: content
return (
<View
className={classNames('at-article', { postcard__isList: props.isList })}
onClick={handleClick}
>
<View className="post-header">
<View className="at-article__h1">{title}</View>
<View className="profile-box">
<AtAvatar circle size="small" image={avatar} />
<View className="at-article__info post-nickName">{nickName}</View>
</View>
</View>
<View className="at-article__content">
<View className="at-article__section">
<View className="at-article__p">{slicedContent}</View>
</View>
</View>
</View>
)
}
PostCard.defaultProps = {
isList: '',
post: [],
}
可以看到這個元件基本不保有自己的狀態,它接收來自父元件的狀態,我們對它的修改主要有下面五個部分:
- 將之前的直接獲取
props.title
和props.content
放到了props.post
屬性中,我們從props.post
屬性中匯出我們需要展示的title
和content
,還要一個額外的user
屬性,它應該是一個物件,儲存著發帖人的使用者屬性,我們使用解構的方法獲取user.avatar
和user.nickName
的值。 - 接著我們看到
return
的元件結構發生了很大的變化,這裡我們為了方便,使用了taro-ui
提供給我們的Article
文章樣式元件,用於展示類似微信公眾號文章頁的一些樣式,可供使用者快速呈現文章內容,可以詳情可以檢視 taro-ui 連結,有了taro-ui
加持,我們就額外的展示了發表此文章的使用者頭像(avatar
)和暱稱(nickName
)。 - 我們還可以看到,這裡我們對原
content
做了一點修改,當PostCard
元件在文章列表中被引用的時候,我們對內容長度進行截斷,當超過 66 字元時,我們就截斷它,並加上省略號...
。 - 最後,我們改動了
handleClick
方法,之前是在跳轉路由的頁面路徑裡直接帶上查詢引數title
和content
,當我們要傳遞的內容多了,這個路徑就會顯得很臃腫,所以這裡我們傳遞此文章對應的id
,這樣可以通過此id
取到完整的post
資料,使路徑保持簡潔,這也是最佳實踐的推薦做法。
接著我們補充一下在 PostCard
元件裡面會用到的樣式,開啟 src/components/PostCard/index.scss
檔案,補充和改進對應的樣式如下:
@import '~taro-ui/dist/style/components/article.scss';
.postcard {
margin: 30px;
padding: 20px;
}
.postcard__isList {
border-bottom: 1px solid #ddd;
padding-bottom: 20px;
}
.post-header {
display: flex;
flex-direction: column;
align-items: center;
}
.profile-box {
display: flex;
flex-direction: row;
align-items: center;
}
.post-nickName {
color: #777;
}
可以看到我們更新了一些樣式,然後引入了 taro-ui
提供給我們的 article
文章樣式。
重構完 “首頁” 頁面元件的所有底層元件,我們開始完成最終的頂層元件,開啟 src/pages/index/index.jsx
檔案,對相應的內容修改如下:
import Taro, { useEffect } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import { AtFab, AtFloatLayout, AtMessage } from 'taro-ui'
import { useSelector, useDispatch } from '@tarojs/redux'
import { PostCard, PostForm } from '../../components'
import './index.scss'
import { SET_POST_FORM_IS_OPENED, SET_LOGIN_INFO } from '../../constants'
export default function Index() {
const posts = useSelector(state => state.post.posts) || []
const isOpened = useSelector(state => state.post.isOpened)
const nickName = useSelector(state => state.user.nickName)
const isLogged = !!nickName
const dispatch = useDispatch()
useEffect(() => {
async function getStorage() {
try {
const { data } = await Taro.getStorage({ key: 'userInfo' })
const { nickName, avatar } = data
// 更新 Redux Store 資料
dispatch({ type: SET_LOGIN_INFO, payload: { nickName, avatar } })
} catch (err) {
console.log('getStorage ERR: ', err)
}
}
getStorage()
})
function setIsOpened(isOpened) {
dispatch({ type: SET_POST_FORM_IS_OPENED, payload: { isOpened } })
}
function handleClickEdit() {
if (!isLogged) {
Taro.atMessage({
type: 'warning',
message: '您還未登入哦!',
})
} else {
setIsOpened(true)
}
}
console.log('posts', posts)
return (
<View className="index">
<AtMessage />
{posts.map((post, index) => (
<PostCard key={index} postId={index} post={post} isList />
))}
<AtFloatLayout
isOpened={isOpened}
title="發表新文章"
onClose={() => setIsOpened(false)}
>
<PostForm />
</AtFloatLayout>
<View className="post-button">
<AtFab onClick={handleClickEdit}>
<Text className="at-fab__icon at-icon at-icon-edit"></Text>
</AtFab>
</View>
</View>
)
}
Index.config = {
navigationBarTitleText: '首頁',
}
可以看到我們上面的內容有以下五處改動:
- 首先我們匯出了
useSelector
鉤子,然後從 Redux store 中獲取了posts
、isOpened
和nickName
等屬性。 - 接著,我們將之前定義在
PostCard
元件上的屬性進行了一次換血,之前是直接傳遞title
和content
屬性,現在我們傳遞整個post
屬性,並且額外傳遞了一個postId
屬性,用於在PostCard
裡面點選跳轉路由時進行標註。 - 接著,我們去掉
PostForm
元件上面的所有屬性,因為我們已經在元件內部定義了它們。 - 接著,我們使用
useEffect
Hooks,在裡面定義並呼叫了getStorage
方法,獲取了我們儲存在storage
裡面的使用者登入資訊,如果使用者登入了,我們 dispatch 一個type
為SET_LOGIN_INFO
的 action,將這份登入資訊儲存在 Redux store 裡面以供後續使用。 - 最後,我們將
AtFab
的onClick
回撥函式替換成handleClickEdit
,在其中對使用者點選進行判斷,如果使用者未登入,那麼彈出警告,告知使用者,如果使用者已經登入,那麼就 dispatch 一個type
為SET_POST_FORM_IS_OPENED
的 action 去設定isOpened
屬性,開啟發帖的彈出層,允許使用者進行發帖操作。
以重構 “文章詳情” 頁結束
最後,讓我們堅持一下,跑贏重構工作的最後一公里?!完成 “文章詳情” 頁的重構。
讓我們開啟 src/pages/post/post.jsx
檔案,對其中的內容作出相應的修改如下:
import Taro, { useRouter } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { useSelector } from '@tarojs/redux'
import { PostCard } from '../../components'
import './post.scss'
export default function Post() {
const router = useRouter()
const { postId } = router.params
const posts = useSelector(state => state.post.posts)
const post = posts[postId]
console.log('posts', posts, postId)
return (
<View className="post">
<PostCard post={post} />
</View>
)
}
Post.config = {
navigationBarTitleText: '帖子詳情',
}
可以看到,上面的檔案做了以下四處修改:
- 我們從
router.params
中匯出了postId
,因為之前我們在PostCard
裡面點選跳轉的路徑引數使用了postId
。 - 接著我們匯入並使用
useSelector
Hooks 獲取了儲存在 Redux store 中的posts
屬性,然後使用上一步獲取到的postId
,來獲取我們最終要渲染的post
屬性。 - 最後,我們將傳給
PostCard
的屬性改成上一步獲取到的post
。
注意
這裡的
console.log
是除錯時使用的,生產環境中建議刪掉。
檢視效果
可以看到,在未登入狀態下,會提示請登入:
在已登入的情況下,發帖子會顯示當前登入使用者的頭像和暱稱:
小結
有幸!到這裡,我們 Redux 重構之旅的萬里長征就跑完了!讓我們來回顧一下我們在這一小節中學到了那些東西。
首先我們講解了使用 Redux 的初衷,接著我們安裝了相關依賴,然後引出了 Redux 三大核心概念:Store、Action、Reducers,接著我們建立了應用需要的兩個 Reducer:
post
和user
;接著我們將將 Redux 和 React 整合起來;因為 Action 是從元件中 dispatch 出來了,所以我們接下來就開始了元件的重構之旅。在重構 “我的” 頁面元件時,我們按照 Redux 的思想,從它的底層元件三個登入按鈕重構開始,接著重構了
LoggedMine
元件,再往上就是Header
元件;重構完Header
元件之後,我們接著從Footer
元件的底層元件Logout
元件開始重構,然後重構了LoginForm
元件,最後是Footer
元件,重構完Header
和Footer
元件,我們開始重構其上層元件mine
頁面元件,自此我們就完成了 “我的” 頁面的重構。在重構 “首頁” 頁面元件時,我們同樣按照 Redux 的思想,從它的底層元件
PostForm
元件開始,接著是PostCard
元件,最後再回到頂層元件index
首頁頁面元件。
在重構 “帖子詳情” 頁面元件時,因為其底層元件 PostCard
已經重構過了,所以我們就直接重構了 post
帖子詳情頁面元件。
能跟著這麼長的文章堅持到這裡,我想給你鼓個掌,也希望你能給自己鼓個掌,我想,我可以非常肯定且自豪的頒佈給你第一名的獎章了?。
終於,這漫長的第五篇結束了。在接下來的文章中,我們將接觸小程式雲後臺開發,並在前端接入後臺資料。
想要學習更多精彩的實戰技術教程?來圖雀社群逛逛吧。
本文所涉及的原始碼都放在了 Github 上,如果您覺得我們寫得還不錯,希望您能給❤️這篇文章點贊+Github倉庫加星❤️哦
本作品採用《CC 協議》,轉載必須註明作者和本文連結