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

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

这是使用 Hooks 版的 Redux 重构状态管理的下篇,在上篇中咱们实现了 user 部分 的状态管理的重构,但受限于篇幅,咱们还剩下 Footer 组件部分没有重构,在这一篇中,咱们将首先实现 Footer 组件的状态管理的重构,接着咱们立刻来实现 post 逻辑的状态管理的重构。前端

若是你不熟悉 Redux,推荐阅读咱们的《Redux 包教包会》系列教程:git

本文所涉及的源代码都放在了 Github 上,若是您以为咱们写得还不错,但愿您能给❤️这篇文章点赞+Github仓库加星❤️哦~

搞定 Footer 的 Redux 化

原本这个小标题我是不想起的,可是由于,是吧,你们上面在没有小标题的状况下看了这么久,可能已经废(累)了,因此我就贴心的加上一个小标题,帮助你定位接下来说解的重心。github

是的接下来,咱们要重构 “个人" tab 页面中的下半部分组件 src/components/Footer/index.js 咱们遵循自顶向下的方式来重构,首先是 src/components/Logout/index.js 文件,咱们打开这个文件,对其中内容做出以下修改:redux

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 状态管理逻辑压平,使得底层组件能够在自身中就解决响应的状态和逻辑问题。小程序

能够看到,咱们上面的文件中主要有五处改动:segmentfault

  • 首先咱们从 @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 中的 nickNameavatar 属性。
提示

这里咱们在组件内定义的 handleLogout 函数和咱们以前在 src/pages/mine/mine.js 中定义的相似,只是使用 dispatch action 的方式替换了重置 nickNameavatar 的部分。数组

搞定完 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 进行状态管理带来好处的一步了,咱们一样将以前在顶层组件中提供的状态压平到了底层组件内部。

能够看到,咱们上面的文件中主要有四处改动:

  • 首先咱们将 formNickNamefiles 等状态放置到 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.jsxsrc/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 来更新本地数据,typeSET_LOGIN_INFO 的 action 用来更新用户登陆信息,typeSET_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 一个 typeSET_IS_OPENED 的 action 来设置 isOpened 属性将 AtFloatLayout 关闭。
  • 接着,咱们开始移除 LogoutLoginForm 组件上再也不须要传递的属性,由于在对应的组件中咱们已经声明了对应的属性了。
  • 最后,咱们删掉以前定义在 Footer 组件内的 formNickNamefiles 等状态,以及再也不须要的 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 方法里面设置 nickNameavatar 的操做替换成了 dispatch 一个 typeSET_LOGIN_INFO 的 action。
  • 接着咱们删除再也不须要的 formNickNamefilesisLogoutisOpened 状态,以及 setLoginInfohandleLogouthandleSetIsOpenedhandleClickhandleSubmit 方法。
  • 最后咱们删除 HeaderFooter 组件上再也不不须要的属性。

大功告成🥈!这里给你颁发一个银牌,以奖励你能一直坚持阅读并跟到这里,咱们这一篇教程很长很长,能跟下来的都不容易,但愿你能在内心或用实际行动给本身鼓鼓掌👏。

小憩一下,恢复精力,整装待发!不少同窗可能很好奇了,为何还只能拿一个银牌呢?那是由于咱们的重构进程才走了一半呀✌️,可是不要担忧,咱们全部新的东西都已经讲完了,接下来就只是一些收尾工做了,当你能坚持到终点的时候,会有惊喜等着你哦!加油吧骚年💪。

开始重构 “首页” 之旅

咱们依然按照以前的套路,从最底层的组件开始重构,首先是咱们的登陆框弹出层 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 文件相似,能够看到,咱们上面的文件中主要有四处改动:

  • 首先咱们将 formTitleformContent 等状态放置到 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 一个 typeSET_POSTS 的 action,将新发表的 post 添加到 Redux store 对应的 posts 数组中。咱们注意到这里咱们使用 useSelector Hooks 从 Redux store 里面获取了 nickNameavatar 属性,并把它们组合到 post.user 属性里,随着 action 的 payload 一块儿被 dispatch,咱们用这个 user 属性标志发帖的用户属性。
    • 清空表单状态。
    • 接着咱们 dispatch 一个 typeSET_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.titleprops.content 放到了 props.post 属性中,咱们从 props.post 属性中导出咱们须要展现的 titlecontent,还要一个额外的 user 属性,它应该是一个对象,保存着发帖人的用户属性,咱们使用解构的方法获取 user.avataruser.nickName 的值。
  • 接着咱们看到 return 的组件结构发生了很大的变化,这里咱们为了方便,使用了 taro-ui 提供给咱们的 Article 文章样式组件,用于展现相似微信公众号文章页的一些样式,可供用户快速呈现文章内容,能够详情能够查看 taro-ui 连接,有了 taro-ui 加持,咱们就额外的展现了发表此文章的用户头像(avatar)和昵称(nickName)。
  • 咱们还能够看到,这里咱们对原 content 作了一点修改,当 PostCard 组件在文章列表中被引用的时候,咱们对内容长度进行截断,当超过 66 字符时,咱们就截断它,并加上省略号 ...
  • 最后,咱们改动了 handleClick 方法,以前是在跳转路由的页面路径里直接带上查询参数 titlecontent ,当咱们要传递的内容多了,这个路径就会显得很臃肿,因此这里咱们传递此文章对应的 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 中获取了 postsisOpenednickName 等属性。
  • 接着,咱们将以前定义在 PostCard 组件上的属性进行了一次换血,以前是直接传递 titlecontent 属性,如今咱们传递整个 post 属性,而且额外传递了一个 postId 属性,用于在 PostCard 里面点击跳转路由时进行标注。
  • 接着,咱们去掉 PostForm 组件上面的全部属性,由于咱们已经在组件内部定义了它们。
  • 接着,咱们使用 useEffect Hooks,在里面定义并调用了 getStorage 方法,获取了咱们保存在 storage 里面的用户登陆信息,若是用户登陆了,咱们 dispatch 一个 typeSET_LOGIN_INFO 的 action,将这份登陆信息保存在 Redux store 里面以供后续使用。
  • 最后,咱们将 AtFabonClick 回调函数替换成 handleClickEdit,在其中对用户点击进行判断,若是用户未登陆,那么弹出警告,告知用户,若是用户已经登陆,那么就 dispatch 一个 typeSET_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:postuser;接着咱们将将 Redux 和 React 整合起来;由于 Action 是从组件中 dispatch 出来了,因此咱们接下来就开始了组件的重构之旅。
  • 在重构 “个人” 页面组件时,咱们按照 Redux 的思想,从它的底层组件三个登陆按钮重构开始,接着重构了 LoggedMine 组件,再往上就是 Header 组件;重构完 Header 组件以后,咱们接着从 Footer 组件的底层组件 Logout 组件开始重构,而后重构了 LoginForm 组件,最后是 Footer 组件,重构完 HeaderFooter 组件,咱们开始重构其上层组件 mine 页面组件,自此咱们就完成了 “个人” 页面的重构。
  • 在重构 “首页” 页面组件时,咱们一样按照 Redux 的思想,从它的底层组件 PostForm 组件开始,接着是 PostCard 组件,最后再回到顶层组件 index 首页页面组件。

在重构 “帖子详情” 页面组件时,由于其底层组件 PostCard 已经重构过了,因此咱们就直接重构了 post 帖子详情页面组件。

能跟着这么长的文章坚持到这里,我想给你鼓个掌,也但愿你能给本身鼓个掌,我想,我能够很是确定且自豪的颁布给你第一名的奖章了🥇。

终于,这漫长的第五篇结束了。在接下来的文章中,咱们将接触小程序云后台开发,并在前端接入后台数据。

想要学习更多精彩的实战技术教程?来 图雀社区逛逛吧。

本文所涉及的源代码都放在了 Github 上,若是您以为咱们写得还不错,但愿您能给❤️这篇文章点赞+Github仓库加星❤️哦

相关文章
相关标签/搜索