<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「HearLing」React學(xué)習(xí)之路-redux、react-redux

          共 12690字,需瀏覽 26分鐘

           ·

          2021-08-05 21:11


          前言

          這篇文章零基礎(chǔ)也可以看,我盡量寫得簡(jiǎn)單易懂了,如果覺得理解起來有點(diǎn)費(fèi)力,也可以先去官網(wǎng)入門。目前我還只是初學(xué)React,只能算入門,如果有哪里說的不對(duì)的,歡迎評(píng)論區(qū)指正,萬分感激~

          一、Redux因何產(chǎn)生?

          首先說它為什么出現(xiàn)

          1.趨勢(shì)所致:JavaScript 單頁應(yīng)用開發(fā)日趨復(fù)雜,「JavaScript 需要管理比任何時(shí)候都要多的 state (狀態(tài))。」

          2.管理不斷變化的 state 非常困難:如果一個(gè) model 的變化會(huì)引起另一個(gè) model 變化,那么當(dāng) view 變化時(shí),就可能引起對(duì)應(yīng) model 以及另一個(gè) model 的變化,依次地,可能會(huì)引起另一個(gè) view 的變化。「state 在什么時(shí)候,由于什么原因,如何變化已然不受控制。」

          二、Redux是干什么的?

          說到底它也只是個(gè)工具,了解一個(gè)工具最開始當(dāng)然是要了解它是做啥的咯。

          官網(wǎng)對(duì)它的定義:Redux 是 JavaScript 狀態(tài)容器,提供可預(yù)測(cè)化的狀態(tài)管理。

          詳細(xì)一些:

          • Redux會(huì)將整個(gè)應(yīng)用狀態(tài)(其實(shí)也就是數(shù)據(jù))存儲(chǔ)到Store
          • Store里面保存一棵狀態(tài)樹(state tree)
          • 組件改變state的唯一方法是通過調(diào)用store的dispatch方法,觸發(fā)一個(gè)action,這個(gè)action被對(duì)應(yīng)的reducer處理,于是state完成更新
          • 組件可以派發(fā)(dispatch)行為(action)給store,而不是直接通知其它組件
          • 其它組件可以通過訂閱store中的狀態(tài)(state)來刷新自己的視圖

          可以結(jié)合這張圖看:

          image.png

          三、Redux怎么用?

          官網(wǎng)實(shí)例(todo)

          「State:」 用了一個(gè)普通對(duì)象描述應(yīng)用中的State,沒有setter(修改器方法)

          {
            todos: [{
              text'Eat food',
              completedtrue
            }, {
              text'Exercise',
              completedfalse
            }],
            visibilityFilter'SHOW_COMPLETED'
          }

          「Action:」 想更新state中的數(shù)據(jù),例如增加todo,需要發(fā)起一個(gè)action。Action就是一個(gè)普通的JavaScript對(duì)象,描述發(fā)生了什么的指示器

          type'ADD_TODO'text'Go to swimming pool' }
          type'TOGGLE_TODO'index1 }
          type'SET_VISIBILITY_FILTER'filter'SHOW_ALL' }

          強(qiáng)制使用 action 來描述所有變化帶來的好處是可以清晰地知道應(yīng)用中到底發(fā)生了什么。如果一些東西改變了,就可以知道為什么變。

          「Reducer:」 把 action 和 state 串起來,reducer 只是一個(gè)接收 state 和 action,并返回新的 state 的函數(shù)。

          //編寫很多小函數(shù)來分別管理 state 的一部分
          function visibilityFilter(state = 'SHOW_ALL', action{
            if (action.type === 'SET_VISIBILITY_FILTER') {
              return action.filter;
            } else {
              return state;
            }
          }

          function todos(state = [], action{
            switch (action.type) {
            case 'ADD_TODO':
              return state.concat([{ text: action.text, completedfalse }]);
            case 'TOGGLE_TODO':
              return state.map((todo, index) =>
                action.index === index ?
                  { text: todo.text, completed: !todo.completed } :
                  todo
             )
            default:
              return state;
            }
          }
          //reducer 調(diào)用上兩個(gè) reducer,進(jìn)而管理整個(gè)應(yīng)用的 state
          function todoApp(state = {}, action{
            return {
              todos: todos(state.todos, action),
              visibilityFilter: visibilityFilter(state.visibilityFilter, action)
            };
          }

          手寫實(shí)戰(zhàn)(TodoList)

          感興趣的可以看一下codesandbox-TodoList例子可能會(huì)比較慢。

          todo這種例子還是比較簡(jiǎn)單的,相當(dāng)于入門,理解Redux工作。

          四、react-redux

          可以看到上面我們并沒有使用到react-redux,雖然能實(shí)現(xiàn)功能,但細(xì)心會(huì)發(fā)現(xiàn)我是直接拿的store,組件多的話個(gè)個(gè)拿store,這樣不好。我來總結(jié)一下不用react-redux可能會(huì)遇到頭痛的問題比如:

          1.store并不是那么顯而易見,一旦組件層級(jí)變得更復(fù)雜,這個(gè)store就會(huì)變得很難控制。

          2.邏輯組件看上去很亂,不清晰的原因state和dispatch沒有各自寫在一起,重復(fù)代碼有點(diǎn)多,不直觀。

          3.React 組件從 Redux store 中讀取數(shù)據(jù),向 store 中分發(fā) actions 更新數(shù)據(jù)還不夠方便。

          Provider

          這個(gè)還是很好理解的,就是把store直接集成到React應(yīng)用的頂層props里面,好處是,所有組件都可以在react-redux的控制之下,所有組件都能訪問到Redux中的數(shù)據(jù)。

          <Provider store={store}>
              <App />
           </Provider>,

          connect

          • 技術(shù)上講,容器組件就是使用store.subscribe()  從 Redux state 樹中讀取部分?jǐn)?shù)據(jù),并通過 props 來把這些數(shù)據(jù)提供給要渲染的組件。
          • 為啥要用它,簡(jiǎn)單來說節(jié)省工作,沒有他得手工開發(fā)容器組件,并為了性能而手動(dòng)實(shí)現(xiàn) React 性能優(yōu)化建議中的 shouldComponentUpdate 方法。
          • 使用 React Redux 庫的 connect() 方法來生成,這個(gè)方法做了性能優(yōu)化來避免很多不必要的重復(fù)渲染。

          connect的使用

          代碼如下:

          const App = connect(mapStateToProps, mapDispatchToProps)(Counter);
          export default App;

          mapStateToProps

          理解這個(gè)單詞mapStateToProps:把state映射到props中去,state就是redux的state啦,props就是react的props咯。

          代碼:

          // Map Redux state to component props
          function mapStateToProps(state{
            return {
              value: state.count
            }
          }

          然后在組件中使用this.props.value就能完成渲染

          class Counter extends Component {
            render() {
              const { value, onIncreaseClick } = this.props;
              return (
                <div>
                  <span>{value}</span>
                  <button onClick={onIncreaseClick}>Increase</button>
                </div>

              );
            }
          }
          export default Counter;

          mapDispatchToProps

          理解這個(gè)單詞mapDispatchToProps:map 各種dispatch 變成props。

          // Map Redux actions to component props
           function mapDispatchToProps(dispatch{
             return {
               onIncreaseClick() => dispatch(increaseAction)
             }
           }
          class Counter extends Component {
            render() {
              const { value, onIncreaseClick } = this.props;
              return (
                <div>
                  <span>{value}</span>
                  <button onClick={onIncreaseClick}>Increase</button>
                </div>

              );
            }
          }
          export default Counter;

          同理也是可以通過this.props.onIncreaseClick調(diào)用dispatch,這樣就不需要在代碼中運(yùn)行dispatch了。

          connect、provider應(yīng)用實(shí)例

          看了上面的介紹,應(yīng)該能比較清楚的了解connect是干什么的了,然后也基本能明白怎么做了,但還是沒有寫哥實(shí)例更清楚直白的了:

          簡(jiǎn)單的點(diǎn)擊增加count的實(shí)例,應(yīng)該還有許多需要優(yōu)化的地方,這里就學(xué)明白connect和provider就好了。

          復(fù)雜一點(diǎn)的todolist的實(shí)例這里用了hooks、connect、provider沒有用react-redux里的hooks鉤子(如果有看不懂的話可以學(xué)學(xué)hooks或者等我有時(shí)間再出一個(gè)class改寫成hooks的文章,還是很簡(jiǎn)單的,只要你專心學(xué))

          五、Hooks下的redux

          如果項(xiàng)目開發(fā)是用的hooks,那很好,你又省了許多力氣,比如計(jì)數(shù)器這個(gè)這種「簡(jiǎn)單」的狀態(tài)管理例子,幾行代碼解決。

          import { useState } from 'react';
          function Example({
            const [count, setCount] = useState(0);
            return (
              <div>
                <p>You clicked {count} times</p>
                <button onClick={() => setCount(count + 1)}>
                  Click me
                </button>
              </div>

            );
          }

          但是我們能完全不用redux狀態(tài)管理了嘛?哈哈哈怎么可能呢

          • 對(duì)于已經(jīng)使用redux的:首先在redux沒有給出對(duì)hooks較好支持之前,大多不會(huì)為了hooks來完全重構(gòu)項(xiàng)目吧,順便一講重構(gòu)可能造成的問題:
            • 失去很多connect()提供的自動(dòng)引用緩存,可能導(dǎo)致性能問題,除非用大量的useCallback()來包裹
            • 如果代碼依賴于mapStateToProps中的ownProps,那么你可能會(huì)使用redux hooks編寫更多代碼,而不能直接拿到這個(gè)屬性。
            • 不能像以前那樣在mapDispatchToProps中,為action creator提供依賴注入
          • 對(duì)于有可能是復(fù)雜應(yīng)用的:許多公司的項(xiàng)目大部分都是用的redux管理狀態(tài),他的許多優(yōu)點(diǎn)比如單一數(shù)據(jù)源、數(shù)據(jù)共享、事務(wù)狀態(tài)、數(shù)據(jù)狀態(tài)I/O和副作用隔離、狀態(tài)回溯以及一系列輔助工具帶來的強(qiáng)大調(diào)試能力等等,使得用redux來管理數(shù)據(jù)流成為更好的選擇。
          • react-redux發(fā)布了新的版本,與之前的contextAPI分離,提供對(duì)hooks的支持,那這不就更香了

          新的redux帶來的改變

          1. 「不再需要使用」  mapStateToPropsmapDispatchToPropsconnect來維護(hù)單獨(dú)的container組件和UI組件,而是在組件中直接使用redux提供的hooks,讀取redux中的state。
          2. 可以將任何現(xiàn)有的自定義「hooks與redux集成」,而不是將通過hooks創(chuàng)建的state,作為參數(shù)傳遞給其他hooks。

          redux對(duì)hooks的支持

          首先介紹幾個(gè)核心:

          • 「useSelector:」 用于從Redux存儲(chǔ)的state中提取值并訂閱該state。
          • 「useDispatch:」 除了讀取store中的state,還能dispatch actions更新store中的state。
          • 「useStore:」 用于獲取創(chuàng)建的store實(shí)例。

          光看簡(jiǎn)介還不是很清楚,一個(gè)個(gè)來說:

          useSelector

          看它的介紹,就很像mapStateToProps,但是

          • 不提供ownProps API,最好用useCallback或useMemo來獲取
          • 和useEffect一樣,如果不提供第二個(gè)參數(shù),每次組件更新就會(huì)重新計(jì)算

          那可能會(huì)存在一些擔(dān)憂,會(huì)不會(huì)新的沒有之前用的mapStateToProps好用呢?那來看看他的一些好處吧:

          • 當(dāng)然是配合hooks寫代碼更「簡(jiǎn)潔」
          • 性能上延續(xù)redux以前的性能優(yōu)化邏輯,「比較props」,如果當(dāng)前的props跟老的props相同,則組件將不會(huì)重新渲染。
          • 批處理更新,使得多個(gè)useSelector()重新計(jì)算出state,組件只會(huì)重新渲染一次,「不用擔(dān)心useSelector重復(fù)渲染問題」

          首先看看以前是怎么寫的:

          //before

          // React component
          class Counter extends Component {
            render() {
              const { value, onIncreaseClick } = this.props;
              return (
                <div>
                  <span>{value}</span>
                  <button onClick={onIncreaseClick}>Increase</button>
                </div>

              );
            }
          }
          export default Counter;

          // Connected Component
          // Map Redux state to component props
          function mapStateToProps(state{return {value: state.count}}
          // Map Redux actions to component props
          function mapDispatchToProps(dispatch{return {onIncreaseClick() => dispatch(increaseAction)}}

          // Connected Component
          const App = connect(mapStateToProps,mapDispatchToProps)(Counter)
          export default App

          然后讓我們用新的useSelect改寫之前寫得計(jì)數(shù)器:

          //after
          const Counter = props=> {
            const { count } = useSelector(
              (state) => ({
                count: state.count
              })
            );
            return (
              <div>
                <span>{count}</span>
              </div>

            );
          }
          export default Counter;

          useDispatch

          之前是使用mapDispatchToProps:

          //before
          // Map Redux actions to component props
            function mapDispatchToProps(dispatch{
              return {
                onIncreaseClick() => dispatch(increaseAction)
              }
            }

          現(xiàn)在使用useDispatch,可以直接在組件中使用,以匿名函數(shù)形式:

          //after
          const dispatch = useDispatch();
          return (
              <div>
                <button onClick={()=>dispatch(increaseAction)}>Increase</button>
              </div>

            );

          由于匿名函數(shù)的性質(zhì),每次重新渲染獲得新的引用,如果作為props傳遞給子組件,那么子組件每次都要重新渲染。

          優(yōu)化的意見是在useCallback中創(chuàng)建這個(gè)匿名函數(shù):

          //after
          import React, { useCallback } from "react";
          import { useDispatch, useSelector } from "react-redux";
          import increaseAction from "./store/action";
          const Counter = props=> {
            const { count } = useSelector(
              (state) => ({
                count: state.count
              })
            );
            const dispatch = useDispatch();
            const onIncreaseClick = useCallback(
              () => dispatch(increaseAction),[dispatch]
            );
            return (
              <div>
                <span>{count}</span>
                <button onClick={onIncreaseClick}>Increase</button>
              </div>

            );
          }
          export default Counter;

          useStore

          在任何需要訪問store的應(yīng)用中,都可以通過usestore來獲取。如果出于某種原因,比如說單元測(cè)試時(shí),想要獲取不同的store,我們可以將store通過新的contextAPI傳遞進(jìn)組件樹中,就像下面這樣:

          import React from 'react';
          import { useStore } from 'react-redux';
          import OtherProvider from './OtherProvider';

          const Component = props => {
            const store = useStore();
            return <OtherProvider store={store}>{props.children}</OtherProvider>
          }

          實(shí)戰(zhàn)

          接著上面已經(jīng)改成hooks的todolist但是還是用的connect的實(shí)例,來重新用react-redux的useSelector和useDispatch實(shí)現(xiàn)。

          基本思想前面介紹的差不多來,這里我就不敗代碼,為了更直觀還是用sandbox雖然不是很快:

          SandBox ------ useSelector、useDispatch實(shí)戰(zhàn)TodoList

          Hooks下的redux總結(jié)

          為什么還是要用redux?簡(jiǎn)單來說:Redux 提供了應(yīng)對(duì)大型應(yīng)用的代碼組織和調(diào)試能力,在程序出錯(cuò)時(shí), 能幫你快速定位問題。

          對(duì)于一些場(chǎng)景的需求hooks沒法解決:

          • 需要保存或者加載狀態(tài)
          • 跨組件共享狀態(tài)
          • 需要與其他組件共享業(yè)務(wù)邏輯或數(shù)據(jù)處理過程

          配合hooks新的redux帶來的不一樣的改變:通過使用useSelector、useDispatch和useStore搭配這hooks寫確實(shí)也是個(gè)不錯(cuò)的嘗試。

          總結(jié)

          作為一個(gè)之前vue技術(shù)棧轉(zhuǎn)react技術(shù)棧的菜鳥來說,還是踩了一些的坑的:比如在有了vuex的基礎(chǔ)之后,然后有沒有理解清楚理解redux,很容易覺得他兩差不多,但實(shí)際還是有挺多區(qū)別的,也是我深入學(xué)習(xí)redux的一個(gè)導(dǎo)火索。

          簡(jiǎn)單的說一下:在 Vuex 中,$store 被直接注入到了組件實(shí)例中,因此可以比較靈活的使用:

          • 使用 dispatch 和 commit 提交更新

          • 通過 mapState 或者直接通過 this.$store 來讀取數(shù)據(jù)

          • 組件中既可以 dispatch action 也可以 commit updates

          在 Redux 中:

          • 我們每一個(gè)組件都需要顯示的用 connect 把需要的 props 和 dispatch 連接起來。
          • Redux 中只能進(jìn)行 dispatch,并不能直接調(diào)用 reducer 進(jìn)行修改。從實(shí)現(xiàn)原理上來說,最大的區(qū)別是兩點(diǎn):

          Redux 使用的是不可變數(shù)據(jù),而Vuex的數(shù)據(jù)是可變的。Redux每次都是用新的state替換舊的state,而Vuex是直接修改。

          Redux 在檢測(cè)數(shù)據(jù)變化的時(shí)候,是通過 diff 的方式比較差異的,而Vuex其實(shí)和Vue的原理一樣,是通過 getter/setter來比較的。

          挖坑

          ???? 后續(xù)可能大概率還會(huì)更新這篇文章,還有些沒寫到,希望這篇文章對(duì)于你學(xué)習(xí)redux有所幫助哦~

          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  renrense在线观看 | 日皮中文字幕 | 国内自拍第一区二区三区 | 中文黄色三级片 | 伊人春色在线 |