中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

React中如何使用Redux

發布時間:2022-06-08 09:14:59 來源:億速云 閱讀:152 作者:zzz 欄目:開發技術

這篇文章主要講解了“React中如何使用Redux”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“React中如何使用Redux”吧!

    Redux 是一種狀態容器 JS 庫,提供可預測的狀態管理,經常和 React 配合來管理應用的全局狀態,進行響應式組件更新。

    Redux 一般來說并不是必須的,只有在項目比較復雜的時候,比如多個分散在不同地方的組件使用同一個狀態。對于這種情況,如果通過 props 層層傳遞,代碼會變得不可維護,這時候我們可以考慮使用 Redux 這類狀態管理庫。

    不使用 Redux 的寫法

    我們創建一個 User 組件,顯示用戶名,并支持設置用戶名。先看看不使用 Redux 的寫法。

    import { Component, createRef } from 'react';
    class User extends Component {
      state = { username: '前端西瓜哥' };
      inputRef = createRef();
    
      setUsername = () => {
        this.setState({ username: this.inputRef.current.value });
      };
      render() {
        return (
          <div>
            <div>用戶名: {this.state.username}</div>
            <input ref={this.inputRef} type="text" />
            <button onClick={this.setUsername}>設置用戶名</button>
          </div>
        );
      }
    }
    export default User;

    下面我們改造一下這個組件,將狀態遷移到 Redux 里。

    最底層的寫法

    Redux 是和框架無關的,我們先看看只用 Redux 庫的寫法。

    demo:codesandbox.io/s/redux-pla&hellip;

    首先我們創建一個 reducer。

    // user_reducer.js
    import { SET_USERNAME } from './constants';
    // 初始值
    const defaultState = {
      name: '前端西瓜哥',
      age: 88
    };
    
    // 用于修改 user 狀態的 reducer
    export const userReducer = (preState = defaultState, action) => {
      switch (action.type) {
        case SET_USERNAME: // type 值都統一放到 constants
          return { ...preState, name: action.payload };
        // 這里還可以根據需要,添加類似 setUserAga 等邏輯
        default:
          return preState;
      }
    };
    // constants.js
    export const SET_USERNAME = 'SET_USERNAME';

    reducer 是一個用于更新狀態的函數,接收原來的狀態 preState 和一個更新動作對象 action。

    action 對象有一個 表示此次操作的描述 type其他數據屬性(通常為 payload)。payload 會以某種方式去計算出一個新的狀態,替換掉 redux 中原來的 state。

    {
     type: 'SET_USERNAME',
     payload: '新用戶名'
    }

    type 通常是一個字符串,比如我們會用 'COUNT_INCREMENT' 來給一個計數器加一,或用 'SET_USERNAME' 來更新用戶名。reducer 會根據不同的 type 來執行不同的更新 state 行為。

    action 的構造我們通常會用一個函數幫忙構建,這種函數稱為 Action Creator:

    // user_action.js
    import { SET_USERNAME } from './constants';
    export const setUsernameAction = (data) => {
      return {
        type: SET_USERNAME,
        payload: data
      };
    };

    有了 reducer,我們可以用它們來構建我們的 store。store 可以訪問所有的保存在 redux 狀態:

    import { combineReducers, createStore } from 'redux';
    import { userReducer } from './user_reducer';
    const store = createStore(
      combineReducers({
        user: userReducer
      })
    );
    export default store;

    combineReducers 可以將多個 reducer 組合在一起,有各自對應的屬性名。比如上面的代碼,我們可以通過 store.getState().user 來拿到用戶對象。

    如果你又新增了 counter 狀態對象,只需再加上 counter: counterReducer,就可以用 store.getState().counter 來拿到這個對象。

    createStore 用于創建應用中所有的 state,然后這些 state 都會存放到這個被返回的 store 里。

    現在我們的 User 組件就變成這樣了:

    import { Component, createRef } from 'react';
    import store from '../store/store';
    import { setUsernameAction } from '../store/user_action';
    class User extends Component {
      inputRef = createRef();
      componentDidMount() {
        store.subscribe(() => {
          this.setState({});
        });
      }
      setUsername = () => {
        store.dispatch(setUsernameAction(this.inputRef.current.value));
      };
    
      render() {
        return (
          <div>
            <div>用戶名: {store.getState().user.name}</div>
            <input ref={this.inputRef} type="text" />
            <button onClick={this.setUsername}>設置用戶名</button>
          </div>
        );
      }
    }
    export default User;
    • store.getState() 可以拿到 state 對象,通過它,我們獲取到其下我們需要的對象,比如 user 對象。

    • store.dispatch(action)  派發 action 對象,觸發狀態的更新。

    • store.subscribe(fn) 訂閱狀態的變化,執行回調函數。這里我們一發現狀態發生了變化,就立刻重新渲染組件。

    Redux 本質是發布訂閱模式,狀態集中在一起,狀態可以通過 store.getState() 訪問,通過 store.dispatch(action) 改變狀態,通過 store.subscribe(fn) 訂閱狀態變化(React 組件監聽到變化后,重新渲染組件)。

    這種寫法是最原始的寫法,可以用在任何框架中。

    缺點很明顯:用到 redux 的組件要訂閱 state 變化,一變化就重新渲染組件。有時候其他組件的 state 變化了,當前組件也會進行不必要的重新渲染

    自己去判斷吧,又太繁瑣,容易寫錯,也容易忘記訂閱。對于忘記訂閱的問題,我們也可以直接把讓根組件來監聽和重新渲染,但這樣性能很差

    接下來西瓜哥要講的 React-Redux 庫可以解決這個問題。它能夠在當前組件用到的特定 state 發生改變時,才重新渲染組件。

    React-Redux

    發現大家都很喜歡在 React 里用 Redux,于是 Facebook 出了一個 React-Redux 庫,讓大家能夠更好更正確地在 React 中使用 Redux。

    React-Redux 配合 connect 高階組件

    我們先看看使用 connect 的寫法。

    demo:codesandbox.io/s/react-red&hellip;

    React-Redux 引入了一個容器組件的概念,這個組件專門負責和 redux 打交道 。容器組件其實是一個高階組件,將真正的 UI 組件做一個封裝,在上面做了以下工作:

    • 將 state 和 dispatch 映射到 props,注入到 UI 組件中

    • 監聽 state 變化,必要時重新渲染 UI 組件。

    高階組件:一個函數,它會接收組件參數,然后返回一個新的組件。高階組件的作用是對真正的 UI 組件做一些復用的邏輯的封裝,通常用于做功能增強。

    隨著 React Hooks 愈發流行,大家現在更喜歡用 React Hooks 來取代高階函數,寫法更優雅。

    const ContainerComponent = connect(
      mapStateToProps,
      mapDispatchToProps,
    )(UIComponent);

    現在開始改造項目。

    我們創建一個 container 文件夾,里面放上 User.jsx 文件,里面寫上如下內容:

    // containers/User.jsx
    import { connect } from 'react-redux';
    import UserUI from '../components/User';
    import { setUsernameAction } from '../store/user_action';
    
    export default connect(
      // mapStateToProps
      (state) => ({ user: state.user }),
      // mapDispatchToProps
      (dispatch) => ({
        setUsername: (newName) => dispatch(setUsernameAction(newName))
      })
    )(UserUI);

    然后記得在使用該容器的地方,傳入我們的 store 對象,如下:

    import UserContainer from './containers/User';
    import store from './store/store';
    import './styles.css';
    export default function App() {
      return <UserContainer store={store} />;
    }

    當然每個容器組件都要傳入 store 未免太麻煩,我們通常會使用另一種做法:使用 redux-react 提供的 Context Provider,包裹住根組件,如下:

    import { Provider } from 'react-redux';
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );

    然后是 UI 組件的改造:

    import { Component, createRef } from 'react';
    class User extends Component {
      inputRef = createRef();
      render() {
        return (
          <div>
            <div>用戶名: {this.props.user.name}</div>
            <input ref={this.inputRef} type="text" />
            <button
              onClick={() => this.props.setUsername(this.inputRef.current.value)}
            >
              設置用戶名
            </button>
          </div>
        );
      }
    }
    export default User;

    UI 組件的 props 會拿到 user 對象、setUsername 方法以及我們注入的 store 對象(如果用 Context 的方式則取不到)。

    React中如何使用Redux

    使用了 connect 后,只有組件用到的 state 改變了,才會觸發組件的更新

    這里有個需要特別注意的地方,就是你要 保證新的狀態對象和舊狀態不相等,這樣才能觸發組件重新渲染,這在處理對象方法時容易出錯。你需要拷貝一個新的對象作為新的狀態,推薦使用擴展運算符的寫法。

    // user_reducer.js
    // 錯誤的寫法,新的 state 依舊指向原來的對象
    preState.name = action.payload;
    return preState;
    // 正確的寫法
    return { ...preState, name: action.payload };

    或者可以考慮使用 immer 這種不可變數據結構庫。

    React-Rudex 配合 React Hooks

    前面我們用了 connect 這么一個高階組件,是為了給 UI 組件增強功能。

    說到增強功能,react-redux 也提供了現在非常流行的 React Hooks 寫法,寫起來更優雅,也是目前西瓜哥我所在公司的做法。

    這里我們就不需要 connect 高階組件了,也就是說不需要容器組件。

    demo:codesandbox.io/s/react-red&hellip;

    // User.js
    import { useEffect, useRef } from 'react';
    import { useDispatch, useSelector } from 'react-redux';
    import { setUsernameAction } from '../store/user_action';
    
    const User = () => {
      // 獲取狀態
      const user = useSelector((state) => state.user);
      // 獲取 dipatch 方法
      const dipatch = useDispatch();
      const inputRef = useRef(null);
    
      return (
        <div>
          <div>用戶名: {user.name}</div>
          <input ref={inputRef} type="text" />
          <button
            onClick={() => {
              const newName = inputRef.current.value;
              dipatch(setUsernameAction(newName));
            }}
          >
            設置用戶名
          </button>
        </div>
      );
    };
    export default User;

    通過 useSelector 我們可以拿到通過上下文綁定的 state,然后從中獲取我們需要用到的狀態

    const user = useSelector((state) => state.user);

    如果有多個,我們可以寫成對象的形式:

    const { user, counter } = useSelector((state) => ({
      user: state.user,
      counter: state.counter
    }));

    是不是有點像 connect 的 mapStateToProps。

    然后是獲取 dispatch 方法:

    const dipatch = useDispatch();

    hook 非常優雅,但我也發現,相比 connect 寫法,我們的 redux 狀態邏輯和組件耦合在一起了。不過一般我們的組件都是業務組件,還是可以接受的。

    Redux Toolkit

    我們可以看到,我們要維護一個狀態,我們要寫 reducer 方法、action creator 方法,還要用一個 contants.js 文件集中式管理所有的 actionType 字符串。

    你發現你寫了非常多的 模板代碼,每加一個 state 就要創建上面這些東西,各個文件里跑來跑去,人都麻了。

    于是 redux 又出了一個工具集庫 Redux Toolkit,來解決這個問題。

    demo:codesandbox.io/s/redux-too&hellip;

    Redux Toolkit 提供了 createSlice 方法,可以幫你用更少的代碼生成配套的 reducer 和 action,而且有很好的可維護性

    // userSlice.js
    import { createSlice } from '@reduxjs/toolkit';
    const userSlice = createSlice({
      name: 'user',
      initialState: {
        name: '前端西瓜哥',
        age: 88
      },
      reducers: {
        setUsername: (state, action) => {
          // 因為 Redux Toolkit 內置使用了 immer,所以可以直接改。
          state.name = action.payload;
        }
      }
    });
    // actions
    export const { setUsername } = userSlice.actions;
    // 獲取自己需要的 state,用在組件的 userSeletor hook 上。
    export const selectUser = (state) => state.user;
    // reducer
    export default userSlice.reducer;

    createSlice 傳入 name(標識符,生成 actions 要用到)、initialState(初始值)、reducers(變成了對象形式)參數,然后返回一個對象。

    這個返回的 slice 對象有 actions 對象屬性,比如上面的代碼,actions 下有一個 setUsername 的方法,執行后會返回 {type: "user/setUsername", payload: "新名字"}

    可以看到 action 的 type 是根據 name 和 reducers 的屬性生產的,確保唯一性。

    slice 還有一個 reducer 對象,其實就是將前面傳入的 reducers 配合自動生成的 action 轉換為了函數的形式。

    createSlice 干了什么事?createSlice 將原來管理一個狀態但代碼卻是分離的 action 和 reducer 集中在了一起,不用自己去起 actionType 的名字

    然后是生成 store 也要改成 configureStore 的寫法:

    // store.js
    import { configureStore } from '@reduxjs/toolkit';
    import userReducer from './userSlice';
    const store = configureStore({
      reducer: {
        user: userReducer
      }
    });
    export default store;

    感謝各位的閱讀,以上就是“React中如何使用Redux”的內容了,經過本文的學習后,相信大家對React中如何使用Redux這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    马鞍山市| 漳州市| 额尔古纳市| 荃湾区| 高雄县| 乡城县| 筠连县| 周口市| 漳平市| 都江堰市| 松溪县| 茂名市| 长岭县| 藁城市| 鱼台县| 阿克苏市| 临猗县| 六安市| 高州市| 广安市| 平果县| 商城县| 新安县| 高密市| 色达县| 五常市| 台中市| 周口市| 武陟县| 南通市| 朔州市| 中山市| 南充市| 淳安县| 河北省| 师宗县| 江达县| 德钦县| 化州市| 赫章县| 平遥县|