一、安裝和配置
React 官方并沒有提供對應(yīng)的狀態(tài)機插件,因此,我們需要下載第三方的狀態(tài)機插件 —— Redux。
1、下載Redux
在終端中定位到項目根目錄,然后執(zhí)行以下命令下載 Redux
npm i redux
2、創(chuàng)建配置文件
在 React 中,不會自動生成狀態(tài)機的相關(guān)配置代碼,因此,需要我們自己手動去創(chuàng)建目錄以及配置文件。
我們可以在 src
的目錄中創(chuàng)建一個 redux
或 store
的目錄,用來存放所有關(guān)于狀態(tài)機的配置文件。然后,在該目錄中創(chuàng)建一個 store.js
文件,作為整個狀態(tài)機的入口文件。
3、配置狀態(tài)機
3.1、創(chuàng)建store倉庫對象
import {legacy_createStore as createStore} from 'redux'
const store=createStore();
3.2、保存數(shù)據(jù)到store中
createStore
方法接收一個函數(shù)作為參數(shù),該函數(shù)的返回值,會保存到 store 倉庫中
const store = createStore((state = 數(shù)據(jù)初始值) => {
? ?return state;
});
通過函數(shù)參數(shù)的默認值的方式,來設(shè)置倉庫數(shù)據(jù)的初始值
3.3、查看倉庫數(shù)據(jù)
在 store
倉庫對象身上,提供了一個 getState()
方法,用來查看倉庫中的所有數(shù)據(jù):
console.log(store.getState());
由于目前沒有任何文件中在引入狀態(tài)機的配置文件,如果我們需要查看倉庫中的數(shù)據(jù),暫時需要在 index.js
中引入 /redux/store.js
文件來讓其運行。
二、Redux核心概念
1、Redux工作流程
2、Redux組成部分
2.1、state
state:狀態(tài),就是我們傳遞的數(shù)據(jù)
2.2、action
action是一個通知對象,里面必須有一個type屬性,表示當前通知的類型,至于其他屬性,你可以任意添加
可以通過store.dispatch(action對象)來更新倉庫中的數(shù)據(jù)
注意:
-
在實際開發(fā)中,更多人喜歡用
action創(chuàng)建函數(shù)
-
在實際開發(fā)中,大多數(shù)情況下,type會被定義成字符串常量
2.3、reducer
reducer本質(zhì)是一個函數(shù),它用來響應(yīng)發(fā)送過來的actions,經(jīng)過處理把state發(fā)送給store
-
在reducer函數(shù)中,需要return返回值,這樣store才能接收到數(shù)據(jù)
-
reducer函數(shù)接收兩個參數(shù),第一個參數(shù)是初始化store,第二個參數(shù)是action
2.4、store
數(shù)據(jù)倉庫,存放組件數(shù)據(jù)的地方。一個項目一般只有一個數(shù)據(jù)倉庫,store可以把action和reducer聯(lián)系在一起。
主要的職責
-
維護應(yīng)用的state
-
提供getState()方法獲取state
-
提供dispatch()方法發(fā)送action
-
通過subscribe()來注冊監(jiān)聽
-
通過subscribe()返回值來注銷監(jiān)聽
三、第一個Redux程序
1、創(chuàng)建action
-
在src目錄下創(chuàng)建一個actions文件夾
-
在該目錄下創(chuàng)建一個index.js文件
-
action是一個通知對象,里面必須有一個type屬性,這里的num屬性是自定義的表示計數(shù)器每次增加的個數(shù)
const incrementAction={
? ?type:'increment',
? ?num:1
}
export {incrementAction}
2、創(chuàng)建reducer
-
在src目錄下創(chuàng)建reducers目錄
-
在該目錄下創(chuàng)建index.js文件,用來構(gòu)建reducer,注意reducer要接收兩個參數(shù)
-
第一個參數(shù)是默認狀態(tài),我們可以定義一個初始化的state,然后進行賦值
-
在函數(shù)里面判斷第二個參數(shù)action的type值是否是我們發(fā)送過的,如果是,我們可以通過return返回新的state
const counterReducer=(state=0,action)=>{
? switch(action.type){
? ? ? ?case 'increment':
? ? ? ? ? ?return state+action.num
? ? ? ?default:
? ? ? ? ? ?return state
? }
}
export {counterReducer}
3、創(chuàng)建store
-
在src目錄下創(chuàng)建一個文件夾store
-
在該目錄下創(chuàng)建index.js文件,用來構(gòu)建store,注意createStore函數(shù)第一個參數(shù)接收的是reducer
import {legacy_createStore as createStore} from 'redux'
import {counterReducer} from '../reducers'
const store=createStore(counterReducer)
export default store
4、在組件中使用
-
創(chuàng)建components文件夾,該目錄下存放自定義組件
-
在該目錄下新建Counter.jsx文件
-
將Counter.jsx引入到App.js文件中
import Counter from "./components/Counter";
function App() {
?return (
? ?<div>
? ? ? ?<Counter></Counter>
? ?</div>
);
}
export default App;
-
調(diào)用dispatch函數(shù),更新倉庫的數(shù)據(jù)
import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'
export default function Counter() {
const increment=()=>{
store.dispatch(incrementAction)
}
return (
<div>
<h1>計數(shù)器:{store.getState()}</h1>
<button onClick={()=>{increment()}}>+</button>
</div>
)
}
-
當組件加載完畢后,調(diào)用 store. subscribe注冊監(jiān)聽,監(jiān)聽state數(shù)據(jù)的變化
import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'
export default function Counter() {
const [count, setCount] = useState(0);
const increment=()=>{
store.dispatch(incrementAction)
}
useEffect(()=>{
store.subscribe(()=>{
console.log('正在監(jiān)控'+store.getState());
setCount({})
})
})
return (
<div>
<h1>計數(shù)器:{store.getState()}</h1>
<button onClick={()=>{increment()}}>+</button>
</div>
)
}
四、redux中action和reducer的優(yōu)化
1、action優(yōu)化
1.1、action creator
我們一般會在頁面里面修改數(shù)據(jù),即,在頁面里面調(diào)用store的dispatch方法。那就意味著action對象中的num字段很大可能是動態(tài)的,即不同的頁面num字段可能是不同的,這樣我們就不能把action寫成一個死的對象,最好是封裝成一個函數(shù),執(zhí)行過后返回一個action通知對象,然后num的值通過函數(shù)的參數(shù)來決定。這樣的函數(shù)我們稱之為action創(chuàng)建函數(shù)(action creator)
const incrementAction=(num)=>{
return{
type:'increment',
num:num
}
}
export {incrementAction}
1.2、type常量
在實際開發(fā)中,type在多數(shù)情況下會被定義成常量,如下所示
-
在src/actions目錄下,新建actionTypes.jsx文件,將所有type類型定義成常量
export const INCREMENT ="increment"
-
在action creator中引入
import {INCREMENT} from './constant'
const incrementAction=(num)=>{
return{
type:INCREMENT,
num:num
}
}
export {incrementAction}
2、reducer優(yōu)化
項目中肯定不止一個數(shù)據(jù),所以state的默認值應(yīng)該是一個對象,而不是其他。而且除了默認值,每當case到一個條件,返回一個新的對象時,應(yīng)該返回一個全新的對象,然后才是你要修改的數(shù)據(jù)(當然這些數(shù)據(jù),如果是引用類型的話,應(yīng)該修改其引用本身,否則界面可能不會更新,盡管數(shù)據(jù)發(fā)生變化了
import { INCREMENT } from '../actions/constant'
const counterReducer = (state = { num: 0 }, action) => {
switch (action.type) {
case INCREMENT:
return {
...state,
num: state.num + action.num
}
default:
return state
}
}
export { counterReducer }
優(yōu)化后,在組件中調(diào)用的代碼如下
import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'
export default function Counter() {
const [count, setCount] = useState(0);
const increment=()=>{
store.dispatch(incrementAction(2))
}
useEffect(()=>{
store.subscribe(()=>{
console.log('正在監(jiān)控'+store.getState());
setCount({})
})
})
return (
<div>
<h1>計數(shù)器:{store.getState()}</h1>
<button onClick={()=>{increment()}}>+</button>
</div>
)
}
五、react-redux概述
為了方便使用,Redux 的作者封裝了一個 React 專用的庫 React-Redux,本文主要介紹它。
React-Redux 將所有組件分成兩大類:UI 組件(presentational component)和容器組件(container component)。
1、UI組件
UI 組件有以下幾個特征。
-
只負責 UI 的呈現(xiàn),不帶有任何業(yè)務(wù)邏輯
-
沒有狀態(tài)(即不使用
this.state
這個變量) -
所有數(shù)據(jù)都由參數(shù)(
this.props
)提供 -
不使用任何 Redux 的 API
2、容器組件
容器組件有以下幾個特征。
-
負責管理數(shù)據(jù)和業(yè)務(wù)邏輯,不負責 UI 的呈現(xiàn)
-
帶有內(nèi)部狀態(tài)
-
使用 Redux 的 API
總之,只要記住一句話就可以了:UI 組件負責 UI 的呈現(xiàn),容器組件負責管理數(shù)據(jù)和邏輯。
React-Redux 規(guī)定,所有的 UI 組件都由用戶提供,容器組件則是由 React-Redux 自動生成。也就是說,用戶負責視覺層,狀態(tài)管理則是全部交給它。
六、react-redux基本使用
1、安裝react-redux
npm i react-redux
npm i redux
2、創(chuàng)建action
-
在constant.jsx中添加INCREMENT
export const INCREMENT="increment";
-
在src/actions/index.jsx中添加incrementAction
import { INCREMENT } from "./constant";
export const incrementAction=num=>({type:INCREMENT,num})
3、創(chuàng)建reducer
-
在src/reducers/index.jsx中添加counterReducer
import {INCREMENT} from '../actions/constant'
const counterReducer=(state={num:0},action)=>{
switch(action.type){
case INCREMENT:
return{
...state,
num:state.num+action.num
}
default:
return state
}
}
export {counterReducer}
4、創(chuàng)建store
在src/store/index.jsx中編寫代碼如下
import {legacy_createStore as createStore} from 'redux'
import {counterReducer} from '../reducers'
const store=createStore(counterReducer)
export default store
5、全局注入store倉庫
-
在index.js中導(dǎo)入Provider組件
import {Provider} from 'react-redux'
-
利用provider組件將整個接口進行包裹
-
給Provider組件設(shè)置store的屬性,該屬性的值就是通過createStore構(gòu)建出來的store實例對象
import ReactDOM from 'react-dom/client'
import App from './App';
import {Provider} from 'react-redux'
import store from './store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
-
Provider
在根組件外面包了一層,這樣一來,App
的所有子組件就默認都可以拿到state
了。
6、組件關(guān)聯(lián)倉庫
由于UI組件不能使用Redux的API所以,如果在組件中如果要使用Redux就必須將UI組件變成容器類組件
React-Redux 提供connect
方法,用于從 UI 組件生成容器組件。connect
的意思,就是將這兩種組件連起來
import React from 'react'
import { connect } from 'react-redux'
function Counter() {
render() {
return (
<div>
</div>
)
}
}
export default connect()(Counter)
7、組件操作倉庫
7.1、獲取倉庫數(shù)據(jù)
connect
方法接收一個回調(diào)函數(shù)作為參數(shù):
const mapStateToProps = () => {
}
export default connect(mapStateToProps)(Counter);
該回調(diào)函數(shù)本身,又可以通過第一個參數(shù)接收到倉庫中的所有數(shù)據(jù)
const mapStateToProps = (state) => {
console.log(state); // 倉庫中所有的數(shù)據(jù)
}
export default connect(mapStateToProps)(Counter);
在該回調(diào)函數(shù)中,返回一個對象,該對象會和組件的 props 進行合并。換句話說,該函數(shù)的返回值會添加到組件的 props 中:
const mapStateToProps = (state) => {
return {
數(shù)據(jù)名: 從 state 中獲取的數(shù)據(jù)值
}
}
export default connect(mapStateToProps)(Counter);
處理完成后,我們就可以在組件的 props 中訪問到對應(yīng)的倉庫數(shù)據(jù)了:
function Counter(props)
render() {
return (
<div>
<h1>計數(shù)器</h1>
<h2>{props.數(shù)據(jù)名}</h2>
</div>
)
}
}
7.2、修改倉庫數(shù)據(jù)
修改倉庫數(shù)據(jù),依然是通過 dispatch()
方法來觸發(fā)修改操作。
在組件中,只要和倉庫關(guān)聯(lián)過,就能在 props 上直接獲取到 dispatch 方法。因此,在組件中可以直接通過 props.dispatch()
方法來觸發(fā)修改數(shù)據(jù)的操作。
import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'
import { connect } from 'react-redux';
function Counter(props) {
const increment=()=>{
props.dispatch(incrementAction(3))
}
return (
<div>
<h1>計數(shù)器:{props.num}</h1>
<button onClick={()=>{increment()}}>+</button>
</div>
)
}
const mapStateToProps = (state) => {
console.log(state); // 倉庫中所有的數(shù)據(jù)
return{
num:state.num
}
}
export default connect(mapStateToProps)(Counter)
七、狀態(tài)機的Hook
針對 React 中的函數(shù)組件,React-Redux 中也提供了第三方的 Hook。
1、useSelector
通過調(diào)用 useSelector
方法,并傳遞一個回調(diào)函數(shù)作為參數(shù),我們可以在這個回調(diào)函數(shù)中獲取到倉庫中的 state。
import { useSelector } from 'react-redux'
useSelector((state) => {
console.log(state); // 倉庫中所有的數(shù)據(jù)
})
然后我們可以在回調(diào)函數(shù)中,將我們需要使用的數(shù)據(jù) return
出來,然后用一個變量來接收:
const data = useSelector((state) => {
return 數(shù)據(jù);
})
后續(xù)組件要使用數(shù)據(jù),就可以直接通過變量進行數(shù)據(jù)的訪問了。
2、useDispatch
調(diào)用 useDispatch()
方法,可以直接獲取到 dispatch()
方法。
import { useDispatch } from 'react-redux'
export default fucntion Test() {
const dispatch = useDispatch();
return (
)
}
如果組件中使用 Hook 來獲取 dispatch
方法的話,就不再需要使用 connect
來對組件和倉庫進行關(guān)聯(lián)了。
獲取到 dispatch
方法后,后續(xù)的使用就和 props.dispatch
的使用一致。
關(guān)鍵代碼
import React from 'react'
import {useDispatch,useSelector} from 'react-redux'
import {incrementAction} from '../../redux/action'
export default function Counter(props) {
const num=useSelector((state)=>{
return state.num
})
const dispatch=useDispatch()
const increment=()=>{
dispatch(incrementAction(3))
}
return (
<div>
<h1>計數(shù)器:{num}</h1>
<button onClick={()=>{increment()}}>+</button>
</div>
)
}
八、reducer拆分
當項目越來越大的時候,需要管理的數(shù)據(jù)也會越來越多,如果所有的數(shù)據(jù)都由一個reducer管理的話,則這個reducer肯定會變得非常的臃腫,且難以維護。所以有必要對reducer做一個拆分,不同功能模塊的數(shù)據(jù)切片,由不同的reducer來管理。假設(shè)現(xiàn)在有兩個模塊,賬戶管理模塊和商品管理模塊,每個模塊都有數(shù)據(jù)需要管理文章來源:http://www.zghlxwxcb.cn/news/detail-694167.html
import {combineReducers} from 'redux'
import counterReducer from './counterReducer'
export default combineReducers({
counter:counterReducer
})
注意:調(diào)用時候需要使用使用到切片的名字才能訪問到,比如文章來源地址http://www.zghlxwxcb.cn/news/detail-694167.html
import React from 'react'
import {useSelector} from 'react-redux'
export default function Counter(props) {
const count=useSelector((state)=>{
console.log(state);
return state.counter.num //state.切片名的key.num
})
return (
<div>
<h1>計數(shù)器:{count}</h1>
</div>
)
}
到了這里,關(guān)于React筆記(八)Redux的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!