Re: 新手让网页 act 起来: Day21 - useReducer vs useState

前天我们介绍了 useReduecer 的基本使用方式,跟 useState 相比起来复杂许多,那究竟为什麽需要有 useReducer 呢?什麽样的情况下适合使用 useReducer?就让我们来了解它吧!

Counter 范例

先来看看上次我们用 useReducer 改写的 Counter

function countReducer(state, action){
  switch (action.type) {
    case 'increment': 
      return state + 1;
    case 'decrement': 
      if (state === 0) return state
      return state - 1;
    default: 
      throw new Error(`no ${action.type} type in counterReducer`)
  }
}

const Counter = () => {
  const [count, setCount] = React.useReducer(countReducer, 0)
  
  const increaseHandler = () => {
    dispatch({type: 'increment'})
  }

  const decreaseHandler = () => {  
    dispatch({type: 'decrement'})
  }

  // return UI ..... 
}

再来看看 useState 版本:

const Counter = () => {
  const [count, setCount] = React.useState(0)
  
  const increaseHandler = () => {
    setCount(count + 1)
  
  }

  const decreaseHandler = () => {
    if (count === 0) return

    setCount(count - 1)
  }
  // return UI ..... 
}

上次为了示范如何使用 useReducer来做计数器,但实际上如果去跟 useState 的情况做比较,用 useReducer 就多写了很多东西,像是:reducer、 switch case 。所以如果是像计数器单一的 state 然後不会影响到其他的 state ,会建议用 useState 就好。

那反过来说,到底什麽状况下一个 state 会影响另一个 state?

分页与排序

假设今天有个需求是,点选页码会切换页面的贴文,然後有个按钮点下去会依照贴文的日期做降幂或升幂排序,同时切回第一页。

const App = () => {
  const [posts, setPosts] = useState([])
  const [page, setPage] = useState(1);
  const [sortBy, setSortBy] = useState({
    category: 'date',
    type: 'desc'
  });
 
  function sortHandler(e){
    const sortCategory = e.target.id
   
    setPage(1)
    setSortBy(prev => {
      const nextType = prev.type === 'desc' ? 'asc' : 'desc'
    
      return {
        ...prev,
        category: sortCategory,
        type: nextType
      }
    })
  }
  
  function pageHandler(e){
    setPage(e.target.id)
  }
  
  useEffect(() => {
    // fetch api and setPost
    
  }, [page, sortBy]);
  
  // return UI ..... 
}

使用 useReducer

function reducer(state, action){
  switch (action.type) {
    case 'change sort': 
      const nextType = state.sortBy.type === 'desc' ? 'asc' : 'desc'
      const nextSortBy = { ...action.sortBy, type: nextType }
      return { ...state, page: 1, sortBy: nextSortBy } 
      
    case 'change page': 
      return { ...state, page: action.page  }
      
    default: 
      throw new Error(`no ${action.type} type in reducer`)
  }
}

const App = () => {
  const [posts, setPosts] = useState([])
  const [payload, dispatch] = useReducer(reducer, {
    page: 1,
    sortBy: {
      category: 'date',
      type: 'desc'
    }
  })
 
  function sortHandler(e){
    const category = e.target.id
   
    dispatch({type: 'change sort', sortBy: { category } }) 
  }
  
  function pageHandler(e){
    dispatch({type: 'change page', page: e.target.id})
  }
  
  useEffect(() => {
    // fetch api and setPost
    
  }, [page, sortBy]);
  
  // return UI ..... 
}

观察一下这两个范例的 sortHandler,用 useState 来实现的话,sortHandler 除了更改 sortBy 的 state 还要 reset page ,如果今天又增加一些需求,需要同时变更的 state 越来越多,或许这更 handler 就会越来越忙。

可以发现如果用 useReducer 来写的话,会将判断移到 reducer ,也不需要再呼叫 setPage,然後 sortHandler 会变的很单纯,就算因为需求的增加而变复杂,在 reducer 中能够做的扩充性也会比单纯使用 useState 来的好,handler 可能也不会因此增加太多事情。

透过 dispatch 的方式,能够相对清楚定义好这些动作该做哪些事情,能让我们增加扩充性外,也让 handler 中的程序码更好阅读。

最後补充,其实 useState 是用 useReducer 做出来的!

以上就是今天的介绍,关於今天的内容有什麽问题都欢迎在下方留言~~

该文章同步发布於:我的部落格


<<:  Day 21- To Do List (8) 利用 HTML Template 呈现资料

>>:  [Day28] Vue3 - 资料绑定

登录档结构和物理位置--一颗四处散落的tree

在上一篇我们了解登录档的意义和由来後,这里要开始解说他的形式了 再次提醒,没有十足的把握请勿做新增修...

道德是资安首要议题

我很喜欢John R. Boatright这本旧教科书《道德与商业行为》。 台湾的中学现在正在推广资...

[Day08 - React Native] 路由,使用 React Native Navigation

React Navigation 在这边使用 Wix/React Native Navigation...

3+4 个 Google 判断付费连结 (Paid Links) 的方法

付费连结(Paid Links)在2010年代曾是个有争议的SEO灰色地带,但在多年後今日的SEO...

寻觅 webpack - 後日谈 - 使用 Snowpack 以原生模组系统建置专案

本系列已集结成书从 0 到 Webpack:学习 Modern Web 专案的建置方式,这是一本完...