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倉庫加星❤️哦

相關文章
相關標籤/搜索