最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React?中使用?Redux?的?4?種寫法小結(jié)

 更新時(shí)間:2022年06月07日 16:37:19   作者:前端西瓜哥  
這篇文章主要介紹了在?React?中使用?Redux?的?4?種寫法,Redux 一般來說并不是必須的,只有在項(xiàng)目比較復(fù)雜的時(shí)候,比如多個(gè)分散在不同地方的組件使用同一個(gè)狀態(tài),本文就React使用?Redux的相關(guān)知識給大家介紹的非常詳細(xì),需要的朋友參考下吧

Redux 是一種狀態(tài)容器 JS 庫,提供可預(yù)測的狀態(tài)管理,經(jīng)常和 React 配合來管理應(yīng)用的全局狀態(tài),進(jìn)行響應(yīng)式組件更新。

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

不使用 Redux 的寫法

我們創(chuàng)建一個(gè) User 組件,顯示用戶名,并支持設(shè)置用戶名。先看看不使用 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}>設(shè)置用戶名</button>
      </div>
    );
  }
}
export default User;

下面我們改造一下這個(gè)組件,將狀態(tài)遷移到 Redux 里。

最底層的寫法

Redux 是和框架無關(guān)的,我們先看看只用 Redux 庫的寫法。

demo:codesandbox.io/s/redux-pla…

首先我們創(chuàng)建一個(gè) reducer。

// user_reducer.js
import { SET_USERNAME } from './constants';
// 初始值
const defaultState = {
  name: '前端西瓜哥',
  age: 88
};

// 用于修改 user 狀態(tài)的 reducer
export const userReducer = (preState = defaultState, action) => {
  switch (action.type) {
    case SET_USERNAME: // type 值都統(tǒng)一放到 constants
      return { ...preState, name: action.payload };
    // 這里還可以根據(jù)需要,添加類似 setUserAga 等邏輯
    default:
      return preState;
  }
};
// constants.js
export const SET_USERNAME = 'SET_USERNAME';

reducer 是一個(gè)用于更新狀態(tài)的函數(shù),接收原來的狀態(tài) preState 和一個(gè)更新動作對象 action。

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

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

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

action 的構(gòu)造我們通常會用一個(gè)函數(shù)幫忙構(gòu)建,這種函數(shù)稱為 Action Creator:

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

有了 reducer,我們可以用它們來構(gòu)建我們的 store。store 可以訪問所有的保存在 redux 狀態(tài):

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

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

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

createStore 用于創(chuàng)建應(yīng)用中所有的 state,然后這些 state 都會存放到這個(gè)被返回的 store 里。

現(xiàn)在我們的 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}>設(shè)置用戶名</button>
      </div>
    );
  }
}
export default User;
  • store.getState() 可以拿到 state 對象,通過它,我們獲取到其下我們需要的對象,比如 user 對象。

  • store.dispatch(action)  派發(fā) action 對象,觸發(fā)狀態(tài)的更新。

  • store.subscribe(fn) 訂閱狀態(tài)的變化,執(zhí)行回調(diào)函數(shù)。這里我們一發(fā)現(xiàn)狀態(tài)發(fā)生了變化,就立刻重新渲染組件。

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

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

缺點(diǎn)很明顯:用到 redux 的組件要訂閱 state 變化,一變化就重新渲染組件。有時(shí)候其他組件的 state 變化了,當(dāng)前組件也會進(jìn)行不必要的重新渲染。

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

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

React-Redux

發(fā)現(xiàn)大家都很喜歡在 React 里用 Redux,于是 Facebook 出了一個(gè) React-Redux 庫,讓大家能夠更好更正確地在 React 中使用 Redux。

React-Redux 配合 connect 高階組件

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

demo:codesandbox.io/s/react-red…

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

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

  • 監(jiān)聽 state 變化,必要時(shí)重新渲染 UI 組件。

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

隨著 React Hooks 愈發(fā)流行,大家現(xiàn)在更喜歡用 React Hooks 來取代高階函數(shù),寫法更優(yōu)雅。

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

現(xiàn)在開始改造項(xiàng)目。

我們創(chuàng)建一個(gè) container 文件夾,里面放上 User.jsx 文件,里面寫上如下內(nèi)容:

// 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} />;
}

當(dāng)然每個(gè)容器組件都要傳入 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)}
        >
          設(shè)置用戶名
        </button>
      </div>
    );
  }
}
export default User;

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

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

這里有個(gè)需要特別注意的地方,就是你要 保證新的狀態(tài)對象和舊狀態(tài)不相等,這樣才能觸發(fā)組件重新渲染,這在處理對象方法時(shí)容易出錯(cuò)。你需要拷貝一個(gè)新的對象作為新的狀態(tài),推薦使用擴(kuò)展運(yùn)算符的寫法。

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

或者可以考慮使用 immer 這種不可變數(shù)據(jù)結(jié)構(gòu)庫。

React-Rudex 配合 React Hooks

前面我們用了 connect 這么一個(gè)高階組件,是為了給 UI 組件增強(qiáng)功能。

說到增強(qiáng)功能,react-redux 也提供了現(xiàn)在非常流行的 React Hooks 寫法,寫起來更優(yōu)雅,也是目前西瓜哥我所在公司的做法。

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

demo:codesandbox.io/s/react-red…

// User.js
import { useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { setUsernameAction } from '../store/user_action';

const User = () => {
  // 獲取狀態(tài)
  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));
        }}
      >
        設(shè)置用戶名
      </button>
    </div>
  );
};
export default User;

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

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

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

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

是不是有點(diǎn)像 connect 的 mapStateToProps。

然后是獲取 dispatch 方法:

const dipatch = useDispatch();

hook 非常優(yōu)雅,但我也發(fā)現(xiàn),相比 connect 寫法,我們的 redux 狀態(tài)邏輯和組件耦合在一起了。不過一般我們的組件都是業(yè)務(wù)組件,還是可以接受的。

Redux Toolkit

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

你發(fā)現(xiàn)你寫了非常多的 模板代碼,每加一個(gè) state 就要?jiǎng)?chuàng)建上面這些東西,各個(gè)文件里跑來跑去,人都麻了。

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

demo:codesandbox.io/s/redux-too…

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

// userSlice.js
import { createSlice } from '@reduxjs/toolkit';
const userSlice = createSlice({
  name: 'user',
  initialState: {
    name: '前端西瓜哥',
    age: 88
  },
  reducers: {
    setUsername: (state, action) => {
      // 因?yàn)?Redux Toolkit 內(nèi)置使用了 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(標(biāo)識符,生成 actions 要用到)、initialState(初始值)、reducers(變成了對象形式)參數(shù),然后返回一個(gè)對象。

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

可以看到 action 的 type 是根據(jù) name 和 reducers 的屬性生產(chǎn)的,確保唯一性。

slice 還有一個(gè) reducer 對象,其實(shí)就是將前面?zhèn)魅氲?reducers 配合自動生成的 action 轉(zhuǎn)換為了函數(shù)的形式。

createSlice 干了什么事?createSlice 將原來管理一個(gè)狀態(tài)但代碼卻是分離的 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;

總結(jié)

簡單總結(jié)一下:

  • 原始寫法,過于簡陋,需要自己通過 store.subscribe(fn) 來判斷一個(gè)組件是否要重新渲染,寫起來麻煩、性能堪憂。

  • 配合 Redux React 庫,通過 connect 來注入 redux 狀態(tài),要多寫一個(gè) connect 高階組件生成的容器組件,但降低了耦合度。Redux React 會只在組件需要的狀態(tài)改變時(shí),重新渲染組件。這里要注意改變時(shí),新舊狀態(tài)不能相同,尤其是對象的情況,否則重新渲染不會工作。

  • 如果你的項(xiàng)目主要使用函數(shù)組件,可以不用 connect,直接用 useSelector 來獲取狀態(tài),以及用 userDispatch 來改變狀態(tài)。非常優(yōu)雅。

  • Redux 又推出了 Redux Toolkit,解決了配置復(fù)雜、需要寫太多模板、需要手動安裝大量相關(guān)包的問題。

到此這篇關(guān)于在 React 中使用 Redux 的 4 種寫法的文章就介紹到這了,更多相關(guān)React使用 Redux內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React Native 混合開發(fā)多入口加載方式詳解

    React Native 混合開發(fā)多入口加載方式詳解

    這篇文章主要介紹了React Native 混合開發(fā)多入口加載方式詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • React中props使用介紹

    React中props使用介紹

    props是組件(包括函數(shù)組件和class組件)間的內(nèi)置屬性,用其可以傳遞數(shù)據(jù)給子節(jié)點(diǎn),props用來傳遞參數(shù)。組件實(shí)例化過程中,你可以向其中傳遞一個(gè)參數(shù),這個(gè)參數(shù)會在實(shí)例化過程中被引用
    2022-12-12
  • React通過父組件傳遞類名給子組件的實(shí)現(xiàn)方法

    React通過父組件傳遞類名給子組件的實(shí)現(xiàn)方法

    React 是一個(gè)用于構(gòu)建用戶界面的 JAVASCRIPT 庫。這篇文章主要介紹了React通過父組件傳遞類名給子組件的方法,需要的朋友可以參考下
    2017-11-11
  • react-router-dom的使用說明

    react-router-dom的使用說明

    這篇文章主要介紹了react-router-dom的使用說明,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 淺談React的React.FC與React.Component的使用

    淺談React的React.FC與React.Component的使用

    本文主要介紹了React的React.FC與React.Component的使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • React腳手架搭建的學(xué)習(xí)

    React腳手架搭建的學(xué)習(xí)

    本文主要介紹了React腳手架搭建的學(xué)習(xí),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • 利用CDN加速react webpack打包后的文件詳解

    利用CDN加速react webpack打包后的文件詳解

    下面小編就為大家分享一篇利用CDN加速react webpack打包后的文件詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02
  • React生命周期與父子組件間通信知識點(diǎn)詳細(xì)講解

    React生命周期與父子組件間通信知識點(diǎn)詳細(xì)講解

    生命周期函數(shù)指在某一時(shí)刻組件會自動調(diào)用并執(zhí)行的函數(shù)。React每個(gè)類組件都包含生命周期方法,以便于在運(yùn)行過程中特定的階段執(zhí)行這些方法
    2022-11-11
  • React常見跨窗口通信方式實(shí)例詳解

    React常見跨窗口通信方式實(shí)例詳解

    這篇文章主要為大家介紹了React常見跨窗口通信方式實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • React 無狀態(tài)組件(Stateless Component) 與高階組件

    React 無狀態(tài)組件(Stateless Component) 與高階組件

    這篇文章主要介紹了React 無狀態(tài)組件(Stateless Component) 與高階組件,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08

最新評論

来安县| 凤山市| 新干县| 旺苍县| 肃南| 安西县| 长子县| 通化县| 新干县| 分宜县| 上杭县| 平度市| 西藏| 缙云县| 临城县| 晴隆县| 贵州省| 阳东县| 辉南县| 施秉县| 鸡西市| 习水县| 南华县| 湾仔区| 霍邱县| 简阳市| 新建县| 彝良县| 寿光市| 库车县| 肥城市| 东丽区| 嘉荫县| 冷水江市| 乐东| 永康市| 永城市| 定边县| 彩票| 寻乌县| 周宁县|