日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
在React中使用Redux的四種寫法

大家好,我是前端西瓜哥。今天我們看看React 中使用 Redux 的 4 種寫法。

創(chuàng)新互聯(lián)主營津南網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,app開發(fā)定制,津南h5重慶小程序開發(fā)搭建,津南網(wǎng)站營銷推廣歡迎津南等地區(qū)企業(yè)咨詢

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

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

不使用 Redux 的寫法

我們創(chuàng)建一個 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 (

用戶名: {this.state.username}




);
}
}
export default User;

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

最底層的寫法

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

demo:https://codesandbox.io/s/redux-plain-demo-bc4kv0。

首先我們創(chuàng)建一個 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 是一個用于更新狀態(tài)的函數(shù),接收原來的狀態(tài) preState 和一個更新動作對象 action。

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

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

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

action 的構(gòu)造我們通常會用一個函數(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 可以將多個 reducer 組合在一起,有各自對應(yīng)的屬性名。比如上面的代碼,我們可以通過 store.getState().user 來拿到用戶對象。

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

createStore 用于創(chuàng)建應(yīng)用中所有的 state,然后這些 state 都會存放到這個被返回的 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 (

用戶名: {store.getState().user.name}




);
}
}
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)聽到變化后,重新渲染組件)。

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

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

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

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

React-Redux

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

React-Redux 配合 connect 高階組件

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

demo:https://codesandbox.io/s/react-redux-with-connect-demo-kfvxt6。

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

  • 將 state 和 dispatch 映射到 props,注入到 UI 組件中。
  • 監(jiān)聽 state 變化,必要時重新渲染 UI 組件。

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

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

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

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

我們創(chuàng)建一個 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 ;
}

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

import { Provider } from 'react-redux';
ReactDOM.render(


,
document.getElementById('root')
);

然后是 UI 組件的改造:

import { Component, createRef } from 'react';
class User extends Component {
inputRef = createRef();
render() {
return (

用戶名: {this.props.user.name}


onClick={() => this.props.setUsername(this.inputRef.current.value)}
>
設(shè)置用戶名


);
}
}
export default User;

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

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

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

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

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

React-Rudex 配合 React Hooks

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

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

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

demo:https://codesandbox.io/s/react-redux-with-hooks-demo-8ixl0h。

// 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 (

用戶名: {user.name}


onClick={() => {
const newName = inputRef.current.value;
dipatch(setUsernameAction(newName));
}}
>
設(shè)置用戶名


);
};
export default User;

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

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

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

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

是不是有點像 connect 的 mapStateToProps。

然后是獲取 dispatch 方法:

const dipatch = useDispatch();

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

Redux Toolkit

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

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

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

demo:https://codesandbox.io/s/redux-toolkit-demo-8x0391

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 內(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ù),然后返回一個對象。

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

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

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

createSlice 干了什么事?createSlice 將原來管理一個狀態(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)來判斷一個組件是否要重新渲染,寫起來麻煩、性能堪憂。
  • 配合 Redux React 庫,通過 connect 來注入 redux 狀態(tài),要多寫一個 connect 高階組件生成的容器組件,但降低了耦合度。Redux React 會只在組件需要的狀態(tài)改變時,重新渲染組件。這里要注意改變時,新舊狀態(tài)不能相同,尤其是對象的情況,否則重新渲染不會工作。
  • 如果你的項目主要使用函數(shù)組件,可以不用 connect,直接用 useSelector 來獲取狀態(tài),以及用 userDispatch 來改變狀態(tài)。非常優(yōu)雅。
  • Redux 又推出了 Redux Toolkit,解決了配置復(fù)雜、需要寫太多模板、需要手動安裝大量相關(guān)包的問題。

網(wǎng)站題目:在React中使用Redux的四種寫法
網(wǎng)頁地址:http://www.dlmjj.cn/article/dpoesph.html