使用react+redux實(shí)現(xiàn)計(jì)數(shù)器功能及遇到問(wèn)題
Redux,本身就是一個(gè)單純的狀態(tài)管理者,我們不追溯它的歷史,從使用角度來(lái)說(shuō):它提供一個(gè)全局的對(duì)象store,store中包含state對(duì)象用以包含所有應(yīng)用數(shù)據(jù),并且store提供了一些reducer方法。這些方法可以自定義,使用調(diào)用者得以改變state的值。state的值僅為只讀,如果需要更改則必須只能通過(guò)reducer。
Redux
- 核心對(duì)象:store
- 數(shù)據(jù)存儲(chǔ):state
- 狀態(tài)更新提交接口:==dispatch==
- 狀態(tài)更新提交參數(shù):帶type和payload的==Action==
- 狀態(tài)更新計(jì)算:==reducer==
- 限制:reducer必須是純函數(shù),不支持異步
- 特性:支持中間件
React + Redux
在recat中不使用redux 時(shí)遇到的問(wèn)題
在react中組件通信的數(shù)據(jù)是單向的,頂層組件可以通過(guò)props屬性向下層組件傳遞數(shù)據(jù),而下層組件不能向上層組件傳遞數(shù)據(jù),要實(shí)現(xiàn)下層組件修改數(shù)據(jù),需要上層組傳遞修改數(shù)據(jù)的方法到下層組件,當(dāng)項(xiàng)目越來(lái)越的時(shí)候,組件之間傳遞數(shù)據(jù)變得越來(lái)越困難
在react中加入redux 的好處
使用redux管理數(shù)據(jù),由于Store獨(dú)立于組件,使得數(shù)據(jù)管理獨(dú)立于組件,解決了組件之間傳遞數(shù)據(jù)困難的問(wèn)題
使用redux
下載redux
npm install redux react-redux
redux 工作流程
- 組件通過(guò) dispatch 觸發(fā)action
- store 接受 action 并將 action 分發(fā)給 reducer
- reducer 根據(jù) action 類型對(duì)狀態(tài)進(jìn)行更改并將更改后的數(shù)據(jù)返回給store
- 組件訂閱了store中的狀態(tài),store中的狀態(tài)更新會(huì)同步到組件
使用react+redux實(shí)現(xiàn)計(jì)數(shù)器
1.創(chuàng)建項(xiàng)目,并安裝 redux
# 如果沒(méi)有安裝react腳手架則執(zhí)行這條命令安裝reate腳手架 npm install -g create-react-app # 創(chuàng)建reate項(xiàng)目 create-react-app 項(xiàng)目名 # 進(jìn)入項(xiàng)目 cd 項(xiàng)目名 # 安裝 redux npm install redux reate-redux
2.引入redux,并根據(jù)開(kāi)始實(shí)現(xiàn)的代碼在react中實(shí)現(xiàn)計(jì)數(shù)器
//index.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import { createStore } from 'redux'; const initialState = { count: 0 } function reducer(state = initialState, action) { switch (action.type) { case 'increment': return { count: state.count + 1 } case 'decrement': return { count: state.count - 1 } default: return state } } const store = createStore(reducer) const increment = { type: 'increment' } const decrement = { type: 'decrement' } function Count() { return <div> <button onClick={() => store.dispatch(increment)}>+</button> <span>{store.getState().count}</span> <button onClick={() => store.dispatch(decrement)}>-</button> </div> } store.subscribe( () => { console.log(store.getState()) ReactDOM.render( <React.StrictMode> <Count /> </React.StrictMode>, document.getElementById('root') ); }) ReactDOM.render( <React.StrictMode> <Count /> </React.StrictMode>, document.getElementById('root') );
明顯以上方式雖然可以實(shí)現(xiàn)計(jì)數(shù)器的功能,但在實(shí)際項(xiàng)目中肯定不能這樣使用,因?yàn)榻M件一般都在單獨(dú)的文件中的,這種方式明顯在其他組件中并不能獲取到Store。
計(jì)數(shù)器案例代碼優(yōu)化-讓store全局可訪問(wèn)
為了解決Store獲取問(wèn)題需要使用react-redux來(lái)解決這個(gè)問(wèn)題,react-redux給我們提供了Provider組件和connect方法
Provide 組件
是一個(gè)組件 可以吧創(chuàng)建出來(lái)的store 放在一個(gè)全局的地方,讓組件可以拿到store,通過(guò)provider組件,將 store 放在了全局的組件可以夠的到的地方 ,provider要求我們放在最外層組件
connect
connect 幫助我們訂閱store中的狀態(tài),狀態(tài)發(fā)生改變后幫助我們重新渲染組件
通過(guò) connect 方法我們可以拿到 store 中的狀態(tài) 把 store 中的狀態(tài)映射到props中
通過(guò) connect 方法可以拿到 dispatch 方法
connect 的參數(shù)為一個(gè)函數(shù) 這個(gè)函數(shù)可以拿到store中的狀態(tài),要求我們這個(gè)函數(shù)必須返回一個(gè)對(duì)象,在這個(gè)對(duì)象中寫(xiě)的內(nèi)容都會(huì)映射給組件的props屬性
connect 調(diào)用后返回一個(gè)函數(shù) 返回的這個(gè)函數(shù)繼續(xù)調(diào)用需要傳入組件告訴connect需要映射到那個(gè)組件的props
新建 Component 文件夾、創(chuàng)建 Count.js 文件
import React from 'react' function Count() { return <div> <button onClick={() => store.dispatch(increment)}>+</button> <span>{store.getState().count}</span> <button onClick={() => store.dispatch(decrement)}>-</button> </div> } export default Count
引入 Provider 組件放置在最外層,并制定store
ReactDOM.render( // 通過(guò)provider組件 將 store 放在了全局的組件可以夠的到的地方 provider要求我們放在最外層組件 <Provider store={store}><Count /></Provider>, document.getElementById('root') );
引入 connect 方法 根據(jù) connect 的使用來(lái)包裹組件
const mapStateProps = state => ({ count: state.count, a: '1' }) // connect 的參數(shù)為一個(gè)函數(shù) 這個(gè)函數(shù)可以拿到store中的狀態(tài),要求我們這個(gè)函數(shù)必須返回一個(gè)對(duì)象,在這個(gè)對(duì)象中寫(xiě)的內(nèi)容都會(huì)映射給組件的props屬性 // connect 調(diào)用后返回一個(gè)函數(shù) 返回的這個(gè)函數(shù)繼續(xù)調(diào)用需要傳入組件告訴connect需要映射到那個(gè)組件的props export default connect(mapStateProps)(Count)
改造 Count 組件把 action 復(fù)制到該文件中
const increment = { type: 'increment' } const decrement = { type: 'decrement' } function Count({count,dispatch}) { return <div> <button onClick={() => {dispatch(increment)}}>+</button> <span>{count}</span> <button onClick={() => {dispatch(decrement)}}>-</button> </div> }
現(xiàn)在項(xiàng)目已經(jīng)可以運(yùn)行了但是Count組件中的 提交Action的那一長(zhǎng)串代碼影響視圖的可讀性,所以代碼還是需要優(yōu)化
計(jì)數(shù)器案例代碼優(yōu)化-讓視圖中的代碼可讀性更高
我們希望視圖中直接調(diào)用一個(gè)函數(shù)這樣視圖代碼可讀性強(qiáng),這個(gè)需要利用connect的第二個(gè)參數(shù),第二個(gè)參數(shù)是一個(gè)函數(shù),這個(gè)函數(shù)的形參就是dispatch方法,要求這個(gè)函數(shù)返回一個(gè)對(duì)象,返回的這個(gè)對(duì)象中的內(nèi)容都會(huì)映射到組件的props屬性上
申明一個(gè)變量為connect中的第二個(gè)參數(shù),在這個(gè)變量中返回執(zhí)行不同action操作的對(duì)象
// connect 的第二個(gè)參數(shù) 這個(gè)參數(shù)是個(gè)函數(shù) 這個(gè)函數(shù)的形參就是dispatch方法 要求返回一個(gè)對(duì)象 這個(gè)對(duì)象中的屬性會(huì)被映射到組件的props上 const mapDispatchToProps = dispatch => ({ increment (){ dispatch({ type: 'increment' }) }, decrement (){ dispatch({ type: 'decrement' }) } }) // connect 的參數(shù)為一個(gè)函數(shù) 這個(gè)函數(shù)可以拿到store中的狀態(tài),要求我們這個(gè)函數(shù)必須返回一個(gè)對(duì)象,在這個(gè)對(duì)象中寫(xiě)的內(nèi)容都會(huì)映射給組件的props屬性 // connect 調(diào)用后返回一個(gè)函數(shù) 返回的這個(gè)函數(shù)繼續(xù)調(diào)用需要傳入組件告訴connect需要映射到那個(gè)組件的props export default connect(mapStateProps, mapDispatchToProps)(Count)
在組件中結(jié)構(gòu)props在視圖中直接綁定事件
function Count({count,increment,decrement}) { return <div> <button onClick={increment}>+</button> <span>{count}</span> <button onClick={decrement}>-</button> </div> }
通過(guò)這次優(yōu)化我們發(fā)現(xiàn) 調(diào)用 dispatch 觸發(fā)action 的方法的代碼都是重復(fù)的,所以還需要繼續(xù)優(yōu)化
優(yōu)化調(diào)用 dispatch 觸發(fā)action 的方法的重復(fù)代碼簡(jiǎn)化
利用 bindActionCreators 來(lái)簡(jiǎn)化 dispatch 觸發(fā) action的操作,bindActionCreators來(lái)幫助我們生成執(zhí)行action動(dòng)作的函數(shù)
bindActionCreators 有兩個(gè)參數(shù),第一個(gè)參數(shù)為 執(zhí)行action的對(duì)象,第二個(gè)參數(shù)為 dispatch方法
分離action操作,新建store/actions/counter.actions.js文件把執(zhí)行action操作單獨(dú)放在這個(gè)文件并導(dǎo)出
export const increment = () => ({type: 'increment'}) export const decrement = () => ({type: 'decrement'})
在Count.js中導(dǎo)入關(guān)于計(jì)數(shù)器的action,用bindActionCreators方法來(lái)生成dispatch執(zhí)行action函數(shù)
import { bindActionCreators } from 'redux' import * as counterActions from './../store/actions/counter.actions' const mapDispatchToProps = dispatch => (bindActionCreators(counterActions, dispatch)) // connect 的參數(shù)為一個(gè)函數(shù) 這個(gè)函數(shù)可以拿到store中的狀態(tài),要求我們這個(gè)函數(shù)必須返回一個(gè)對(duì)象,在這個(gè)對(duì)象中寫(xiě)的內(nèi)容都會(huì)映射給組件的props屬性 // connect 調(diào)用后返回一個(gè)函數(shù) 返回的這個(gè)函數(shù)繼續(xù)調(diào)用需要傳入組件告訴connect需要映射到那個(gè)組件的props export default connect(mapStateProps, mapDispatchToProps)(Count)
代碼優(yōu)化到這里我們發(fā)現(xiàn),redux的代碼與組件融合在一起,所以我需要拆分成獨(dú)立的,為什么要抽離redux呢?因?yàn)槲覀円屛覀兊拇a結(jié)構(gòu)更加合理
重構(gòu)計(jì)數(shù)器,把redux相關(guān)代碼抽離
把reducer函數(shù)抽離為單獨(dú)的文件、把創(chuàng)建store抽離到單獨(dú)的文件中
因?yàn)樵趓educer 和 actions中我們都寫(xiě)了字符串,但是字符串沒(méi)有提示所以我們把字符串定義成常量防止我們出現(xiàn)單詞錯(cuò)誤這種低級(jí)錯(cuò)誤,新建 src/store/const/counter.const.js 文件
export const INCREMENT = 'increment' export const DECREMENT = 'decrement'
新建 src/store/reducers/counter.reducers.js 文件把 reducer 函數(shù)抽離到此文件中
import { INCREMENT, DECREMENT} from './../const/counter.const' const initialState = { count: 0 } // eslint-disable-next-line import/no-anonymous-default-export export default (state = initialState, action) => { switch (action.type) { case INCREMENT: return { count: state.count + 1 } case DECREMENT: return { count: state.count - 1 } default: return state } }
更改actions中的字符串為引入變量
import { INCREMENT, DECREMENT} from './../const/counter.const' export const increment = () => ({type: INCREMENT}) export const decrement = () => ({type: DECREMENT})
創(chuàng)建src/store/index.js文件 ,在這個(gè)文件中創(chuàng)建store 并導(dǎo)出
import { createStore } from 'redux'; import reducer from './reducers/counter.reducers' export const store = createStore(reducer)
在引入store的文件中改變?yōu)闆_項(xiàng)目中store文件中引入store
import React from 'react'; import ReactDOM from 'react-dom'; import Count from './components/Count'; import { store } from './store' import { Provider } from 'react-redux' /** * react-redux 讓react 和 redux 完美結(jié)合 * Provider 是一個(gè)組件 可以吧創(chuàng)建出來(lái)的store 放在一個(gè)全局的地方 讓組件可以拿到store * connect 是一個(gè)方法 */ ReactDOM.render( // 通過(guò)provider組件 將 store 放在了全局的組件可以夠的到的地方 provider要求我們放在最外層組件 <Provider store={store}><Count /></Provider>, document.getElementById('root') );
為action 傳遞參數(shù),對(duì)計(jì)數(shù)器案例做擴(kuò)展
這個(gè)計(jì)數(shù)器案例已經(jīng)實(shí)現(xiàn)了點(diǎn)擊按鈕加一減一操作了,現(xiàn)在有個(gè)新需求我們需要加減一個(gè)數(shù)值例如加五減五
這就需要對(duì)action傳遞參數(shù)了
在視圖中按鈕綁定函數(shù)傳入?yún)?shù)
function Count({count,increment,decrement}) { return <div> <button onClick={() => increment(5)}>+</button> <span>{count}</span> <button onClick={() => decrement(5)}>-</button> </div> }
在dispacth執(zhí)行action動(dòng)作時(shí)接受參數(shù)并傳入到action中
export const increment = payload => ({type: INCREMENT, payload}) export const decrement = payload => ({type: DECREMENT, payload})
在reducers中接收參數(shù)并作相應(yīng)處理
export default (state = initialState, action) => { switch (action.type) { case INCREMENT: return { count: state.count + action.payload } case DECREMENT: return { count: state.count - action.payload } default: return state } }
原文地址:https://kspf.xyz/archives/10/
到此這篇關(guān)于在react中使用redux并實(shí)現(xiàn)計(jì)數(shù)器案例的文章就介紹到這了,更多相關(guān)react redux實(shí)現(xiàn)計(jì)數(shù)器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React Draggable插件如何實(shí)現(xiàn)拖拽功能
這篇文章主要介紹了React Draggable插件如何實(shí)現(xiàn)拖拽功能問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07React?Refs?的使用forwardRef?源碼示例解析
這篇文章主要為大家介紹了React?之?Refs?的使用和?forwardRef?的源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11React實(shí)現(xiàn)createElement 和 cloneElement的區(qū)別
本文詳細(xì)介紹了React中React.createElement和React.cloneElement兩種方法的定義、用法、區(qū)別及適用場(chǎng)景,具有一定的參考價(jià)值,感興趣的可以了解一下2024-09-09react實(shí)現(xiàn)記錄拖動(dòng)排序
這篇文章主要介紹了react實(shí)現(xiàn)記錄拖動(dòng)排序的相關(guān)資料,需要的朋友可以參考下2023-07-07使用webpack5從0到1搭建一個(gè)react項(xiàng)目的實(shí)現(xiàn)步驟
這篇文章主要介紹了使用webpack5從0到1搭建一個(gè)react項(xiàng)目的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12react創(chuàng)建項(xiàng)目啟動(dòng)報(bào)錯(cuò)的完美解決方法
這篇文章主要介紹了react創(chuàng)建項(xiàng)目啟動(dòng)報(bào)錯(cuò)的完美解決方法,全稱為Node Package Manager,是隨同NodeJS一起安裝的包管理工具,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08react中使用heatmap.js實(shí)現(xiàn)熱力圖的繪制
heatmap.js?是一個(gè)用于生成熱力圖的?JavaScript?庫(kù),React?是一個(gè)流行的?JavaScript?庫(kù),用于構(gòu)建用戶界面,本小編給大家介紹了在React?應(yīng)用程序中使用heatmap.js實(shí)現(xiàn)熱力圖的繪制的示例,文中通過(guò)代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12