您的位置:首页 > 移动开发 > 微信开发

Taro 小程序开发大型实战(五):使用 Hooks 版的 Redux 实现应用状态管理(下篇)

2020-04-08 18:36 966 查看

欢迎继续阅读《Taro 小程序开发大型实战》系列,前情回顾:

这是使用 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仓库加星❤️哦

  • 点赞
  • 收藏
  • 分享
  • 文章举报
一只图雀 发布了32 篇原创文章 · 获赞 21 · 访问量 4万+ 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐