<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在 React 專案中未加入 Redux 時的問題
在 React 中元件通訊的資料流是單向的,頂層元件可以通過 props 屬性向下層元件傳遞資料,而下層元件不能直接向上層元件傳遞資料。要實現下層元件修改上層元件的資料,需要上層元件傳遞修改資料的方法到下層元件。當專案越來越大的時候,元件之間傳遞資料以及傳遞修改資料的方法變得越來越困難。
使用 Redux 管理資料,由於 Store 獨立於元件,使得資料管理獨立於元件,解決了元件與元件之間傳遞資料困難的問題。
在 react 專案中使用 redux 要下載兩個模組
npm install redux react-redux
在 React 中 Redux 的工作流程有些變化:
React 實現
建立專案安裝模組
# 建立專案(React 17 版本) npx create-react-app myapp # 安裝 redux cd myapp npm install redux
刪掉無用的檔案
├─ src │ ├─ App.css │ ├─ App.test.js │ ├─ index.css │ ├─ logo.svg │ ├─ reportWebVitals.js │ └─ setupTests.js
初步實現
// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; 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 Counter() { return <div> <button onClick={() => store.dispatch(increment)}>+</button> <span>{store.getState().count}</span> <button onClick={() => store.dispatch(decrement)}>-</button> </div> } store.subscribe(() => { ReactDOM.render( <React.StrictMode> <Counter /> </React.StrictMode>, document.getElementById('root') ); }) console.log(store.getState()) ReactDOM.render( <React.StrictMode> <Counter /> </React.StrictMode>, document.getElementById('root') );
開發時我們會把元件寫在單獨的檔案中,如果將 Counter 元件單獨提取,就無法存取 store 物件以及一些其它的問題,所以需要使用 redux。
安裝 react-redux
npm install react-redux
react-redux 用於讓 react 和 redux 完美結合,它僅僅提供兩個內容:
修改程式碼
// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import { createStore } from 'redux' import Counter from './components/Counter' import { Provider } from 'react-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) ReactDOM.render( // 通過 provider 元件將 store 放在了全域性,供所有元件可以存取 <React.StrictMode> <Provider store={store}> <Counter /> </Provider> </React.StrictMode>, document.getElementById('root') );
提取的 Counter 元件
// src/components/Counter.js import { connect } from 'react-redux' function Counter(props) { return <div> <button onClick={() => props.dispatch({ type: 'increment' })}>+</button> <span>{props.count}</span> <button onClick={() => props.dispatch({ type: 'decrement' })}>-</button> </div> } const mapStateToProps = state => ({ count: state.count }) export default connect(mapStateToProps)(Counter)
使用 connect 第二個引數簡化元件
// src/components/Counter.js import { connect } from 'react-redux' function Counter({count, increment, decrement}) { return <div> <button onClick={increment}>+</button> <span>{count}</span> <button onClick={decrement}>-</button> </div> } const mapStateToProps = state => ({ count: state.count }) const mapDispatchToProps = dispatch => ({ increment () { dispatch({ type: 'increment' }) }, decrement () { dispatch({ type: 'decrement' }) } }) export default connect(mapStateToProps, mapDispatchToProps)(Counter)
觸發 Action 的方法 increment
和 decrement
的內容基本是一樣的,redux 提供 bindActionCreators 方法生成一個函數,來簡化這種重複性程式碼。
// src/components/Counter.js import { connect } from 'react-redux' import { bindActionCreators } from 'redux' function Counter({count, increment, decrement}) { return <div> <button onClick={increment}>+</button> <span>{count}</span> <button onClick={decrement}>-</button> </div> } const mapStateToProps = state => ({ count: state.count }) const mapDispatchToProps = dispatch => ({ ...bindActionCreators({ increment() { return { type: 'increment' } }, decrement() { return { type: 'decrement' } } }, dispatch) }) export default connect(mapStateToProps, mapDispatchToProps)(Counter)
此時還沒有達到簡化的效果,可以將 actionCreators 提取到單獨的檔案中。
// srcstoreactionscounter.action.js export const increment = () => ({ type: 'increment' }) export const decrement = () => ({ type: 'decrement' })
// src/components/Counter.js import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import * as counterActions from '../store/actions/counter.action' function Counter({count, increment, decrement}) { return <div> <button onClick={increment}>+</button> <span>{count}</span> <button onClick={decrement}>-</button> </div> } const mapStateToProps = state => ({ count: state.count }) const mapDispatchToProps = dispatch => bindActionCreators(counterActions, dispatch) export default connect(mapStateToProps, mapDispatchToProps)(Counter)
為了繼續演示 Redux 的相關內容,將與 Redux 相關的程式碼從 src/index.js
中抽離出去,讓專案結構更加合理。
編輯器有提示,避免寫錯
編輯器自動插入模組引入的程式碼
將 Actions 的 type 抽象成模組中的成員
// srcstoreactionscounter.action.js import { DECREMENT, INCREMENT } from "../const/counter.const" export const increment = () => ({ type: INCREMENT }) export const decrement = () => ({ type: DECREMENT })
將 reducer 函數抽離到一個檔案中
// srcstorereducerscounter.reducer.js import { DECREMENT, INCREMENT } from "../const/counter.const" 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 } } export default reducer
將建立 store 的程式碼手裡到一個檔案中
// srcstoreindex.js import { createStore } from 'redux' import reducer from './reducers/counter.reducer' export const store = createStore(reducer)
修改後的 src/index.js
// src/index.js import React from 'react' import ReactDOM from 'react-dom' import Counter from './components/Counter' import { Provider } from 'react-redux' import { store } from './store' ReactDOM.render( // 通過 provider 元件將 store 放在了全域性,供所有元件可以存取 <React.StrictMode> <Provider store={store}> <Counter /> </Provider> </React.StrictMode>, document.getElementById('root') )
當前計數器對數位進行遞增/減的數值為 1,現在通過給 Action 傳遞引數,允許自定義數值。
傳遞引數:
// src/components/Counter.js function Counter({ count, increment, decrement }) { // 修改 Counter 元件中呼叫 increment decrement 函數的地方 return ( <div> <button onClick={() => increment(5)}>+</button> <span>{count}</span> <button onClick={() => decrement(5)}>-</button> </div> ) }
接收引數:
// srcstoreactionscounter.action.js import { DECREMENT, INCREMENT } from "../const/counter.const" export const increment = payload => ({ type: INCREMENT, payload }) export const decrement = payload => ({ type: DECREMENT, payload })
處理引數:
// srcstorereducerscounter.reducer.js import { DECREMENT, INCREMENT } from "../const/counter.const" const initialState = { count: 0 } function reducer(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 } } export default reducer
在頁面中顯示兩個按鈕:
Modal 元件
// srccomponentsModal.js function Modal() { const styles = { width: 200, height: 200, position: 'absolute', left: '50%', top: '50%', marginLeft: -100, marginTop: -100, backgroundColor: 'skyblue' } return ( <div> <button>顯示</button> <button>隱藏</button> <div style={styles}></div> </div> ) } export default Modal
修改 src/index.js
// src/index.js import React from 'react' import ReactDOM from 'react-dom' import App from './App' import { Provider } from 'react-redux' import { store } from './store' ReactDOM.render( // 通過 provider 元件將 store 放在了全域性,供所有元件可以存取 <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>, document.getElementById('root') )
修改 src/App.js
// srcApp.js import Counter from './components/Counter' import Modal from './components/Modal' function App() { return ( <div> <Counter /> <Modal /> </div> ) } export default App
在 reducer 中新增顯示狀態的屬性
// srcstorereducerscounter.reducer.js import { DECREMENT, INCREMENT } from "../const/counter.const" const initialState = { count: 0, showStatus: false } function reducer(state = initialState, action) {...} export default reducer
在元件中使用狀態
// srccomponentsModal.js import { connect } from 'react-redux' function Modal({ showStatus }) { const styles = { width: 200, height: 200, position: 'absolute', left: '50%', top: '50%', marginLeft: -100, marginTop: -100, backgroundColor: 'skyblue', display: showStatus ? 'block' : 'none' } return ( <div> <button>顯示</button> <button>隱藏</button> <div style={styles}></div> </div> ) } const mapStateToProps = state => ({ showStatus: state.showStatus }) export default connect(mapStateToProps)(Modal)
定義 Action 的 type
// srcstoreconstmodal.const.js export const SHOWMODAL = 'showModal' export const HIDEMODAL = 'hideModal'
定義生成 Action 的函數
// srcstoreactionsmodal.actions.js import { HIDEMODAL, SHOWMODAL } from "../const/modal.const" export const show = () => ({ type: SHOWMODAL }) export const hide = () => ({ type: HIDEMODAL })
建立觸發 Action 的方法並使用
// srccomponentsModal.js import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import * as modalActions from '../store/actions/modal.actions' function Modal({ showStatus, show, hide }) { const styles = { width: 200, height: 200, position: 'absolute', left: '50%', top: '50%', marginLeft: -100, marginTop: -100, backgroundColor: 'skyblue', display: showStatus ? 'block' : 'none' } return ( <div> <button onClick={show}>顯示</button> <button onClick={hide}>隱藏</button> <div style={styles}></div> </div> ) } const mapStateToProps = state => ({ showStatus: state.showStatus }) const mapDispatchToProps = dispatch => bindActionCreators(modalActions, dispatch) export default connect(mapStateToProps, mapDispatchToProps)(Modal)
修改 reducer 函數,處理變更:
// srcstorereducerscounter.reducer.js import { DECREMENT, INCREMENT } from '../const/counter.const' import { HIDEMODAL, SHOWMODAL } from '../const/modal.const' const initialState = { count: 0, showStatus: false } function reducer(state = initialState, action) { switch (action.type) { case INCREMENT: // reducer 返回的物件會替換 store 中的狀態物件,所以要將其它狀態也包含進去 return { ...state, count: state.count + action.payload } case DECREMENT: return { ...state, count: state.count - action.payload } case SHOWMODAL: return { ...state, showStatus: true } case HIDEMODAL: return { ...state, showStatus: false } default: return state } } export default reducer
注意:reducer 返回的物件會替換 store 中的狀態物件,所以要將其它狀態也包含進去
在 reducer 函數中匹配了所有狀態的變更,當專案越來越大,狀態越來越多時,管理起來就很麻煩。
所以要將 rreducer 函數進行拆分。
將 modal 拆分出去
// srcstorereducersmodal.reducer.js import { HIDEMODAL, SHOWMODAL } from '../const/modal.const' const initialState = { show: false } const reducer = (state = initialState, action) => { switch (action.type) { case SHOWMODAL: return { ...state, showStatus: true } case HIDEMODAL: return { ...state, showStatus: false } default: return state } } export default reducer
// srcstorereducerscounter.reducer.js import { DECREMENT, INCREMENT } from '../const/counter.const' const initialState = { count: 0, } function reducer(state = initialState, action) { switch (action.type) { case INCREMENT: // reducer 返回的物件會替換 store 中的狀態物件,所以要將其它狀態也包含進去 return { ...state, count: state.count + action.payload } case DECREMENT: return { ...state, count: state.count - action.payload } default: return state } } export default reducer
合併 reducer 需要藉助 redux 提供的 combineReducers
方法。
combineReducers 把一個由多個不同 reducer 函數作為 value 的 object 物件,合併成一個最終的 reducer 函數,然後就可以對這個 reducer 呼叫 createStore 方法。
合併後的 reducer 可以呼叫各個子 reducer,並把它們返回的結果合併成一個 state 物件。
由 combineReducers() 返回的 state 物件,會將傳入的每個 reducer 返回的 state 按傳遞給 combineReducers() 時對應的 key 進行命名。
// srcstorereducersroot.reducer.js import { combineReducers } from 'redux' import CounterReducer from './counter.reducer' import ModalReducer from './modal.reducer' // 合併後的 store 為 { counter: { count: 0 }, modal: { showStatus: false } } export default combineReducers({ counter: CounterReducer, modal: ModalReducer })
// srcstoreindex.js import { createStore } from 'redux' import RootReducer from './reducers/root.reducer' export const store = createStore(RootReducer)
因為 store 中的資料結構發生變化,所以還需要調整下元件中獲取狀態的地方
// srcstoreindex.js import { createStore } from 'redux' import RootReducer from './reducers/root.reducer' export const store = createStore(RootReducer)
// srccomponentsModal.js const mapStateToProps = state => ({ showStatus: state.modal.showStatus })
到此這篇關於在 React 中使用 Redux 解決的問題的文章就介紹到這了,更多相關React Redux 案例內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45