網站首頁 編程語言 正文
這比較適合看過redux但看了跟沒看一樣的同學,幫助記憶理清里面的基礎知識
一. Redux
1. Store
Store是保存數據的地方,可以把它看做一個容器。整個應用只能有一個Store。
Redux提供createStore
這個函數生成store
import { createStore } from 'redux';
const store = createStore(fn);
// store也可以接受第二個參數:state的初始值,他會覆蓋reducer的state參數中的默認初始值
const store = createStore(fn, initState);
2. State
當你想拿到某個時刻的數據state時,需要生成對應的store快照。
const state = store.getState();
3. Action
由于用戶觸碰不到代碼(State),只能通過觸碰View來通知代碼服務(store)改變State,這個由View發起的通知就是Action。
Action是一個對象,其中type
是必須的,表示Action的名稱。其他屬性可以自由設置。
const action = {
type: "ADD",
payload: "data"
}
可以理解為,要觸發一個ADD
行為,攜帶的參數是payload
。
3.3 Action Creator
類似于工廠模式,Action肯定會有很多種消息,因此可以寫一個Action Creator來生產一個Action
function AddCreator(text) {
return {
type: "ADD",
payload: text
}
}
4. Dispatch
你可以將action理解為一個通知單,那么View如何發出這個通知單給代碼服務(store)呢,就是通過dispatch
。
只要給dispatch傳入一個action參數,View就把通知單發出去了!
import { createStore } from 'redux';
const store = createStore(fn);
const action = {
type: "ADD",
payload: "data"
}
store.dispatch(action);
5.Reducer
View把通知單傳給代碼服務后,代碼服務(Store)總要根據action中的內容去進行相應的處理的。reducer就是用來識別action并進行相應處理的處理器。他接收當前State和Action作為參數,返回一個新的State。
//其實就是先去判斷action要執行哪個操作,再把對應的state更新并返回
function reducer = (state = initState, action) => {
switch(action.type) {
case 'ADD':
return state + action.payload;
default:
return state
}
}
注意這個處理器是store的,也就是說,store的創建是伴隨reducer參數來初始化的。
import { createStore } from 'redux';
const store = createStore(reducer);
6. Subscribe
Store也提供了subscribe方法來設置監聽函數,一旦State發生變化,就會自動執行這個函數。
store.subscribe
返回一個函數,調用這個函數可以解除監聽。
const listener = function() {
console.log(store.getState())
}
let unsubscribe = store.subscribe(listener);
// 解除監聽
unsubscribe();
過度------------簡易使用
你知道的,畢竟store不是react組件中的state,store中state的更新并不會讓View也同時變化,你需要手動調用render重新渲染。
const Counter = ({ value, onIncrement, onDecrement }) => (
<div>
<h1>{value}</h1>
<button onClick={onIncrement}>+</button>
<button onClick={onDecrement}>-</button>
</div>
);
const reducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
};
const store = createStore(reducer);
const render = () => {
ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => store.dispatch({type: 'INCREMENT'})}
onDecrement={() => store.dispatch({type: 'DECREMENT'})}
/>,
document.getElementById('root')
);
};
render();
// 通過監聽state的改變來更新視圖
store.subscribe(render);
7. 中間件
當我們想要在store的整個流程中添加一些其他功能時,我們該往哪里添加?
處理流程中涉及邏輯進程的主要就是dispatch和reducer了,但reducer要求是純函數,不能涉及副作用,那只能在dispatch中做手腳了。
先看一下想添加一個日志功能,并將更新后的state打印出來該如何實現:
let next = store.dispatch;
store.dispatch = function changeDispatch(action) {
console.log('dispatching', action);
next(action);
console.log('next state', store.getState());
}
上面對store.dispatch
進行了改造,添加了一些功能,這就是中間件的雛形。中間件其實就是一個函數。
7.1 applyMiddlewares()
Redux提供了原生方法applyMiddlewares
來加入中間件,增強store.dispatch
的功能。
他可以將中間件組成一個數組,然后依次執行。
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();
const store = createStore(
reducer,
initial_state,
applyMiddleware(logger, ...otherMiddlewares)
);
8. 如何處理異步操作
Action發出后,過一段時間再執行Reducer,這就是異步。
那么Reducer作為純函數肯定是不可以在這里添加處理異步邏輯的,那么就得在store.dispatch中做手腳了。
想一想,如果不能同步的進行dispatch(異步) -> reducer
,那不如將dispatch分為兩步走,dispatch(異步獲取) -> dispatch(同步發送action) -> reducer
。
// 操作發起時的 Action
// 操作成功時的 Action
// 操作失敗時的 Action
具體如何改造多次的dispatch可以使用redux-thunk
或者redux-promise
,他們處理后,dispatch可以接受不同的參數,不再局限于action對象。
二. React-Redux
回憶下之前的Redux,state更新時我們需要設置監聽函數實時re-render才能更新View,想要在其他組件中使用store都得引入一下。
React-Redux解決了上述的痛點:
- 提供Provider把state注入全局,這樣子組件就不用單獨引入了
- 響應式。無需我們手動監聽,state的更新會自動幫我們更新View
- 通過connnct把state和dispatch注入到當前組件的props上
1. Provider
React-Redux提供了組件,使得整個app能夠訪問到store中的數據,Provider是用context封裝的。
2. connect
React-Redux將組件分為兩種,一種是UI組件,另一種是容器組件。
UI組件只通過接受props來顯示UI,容器組件負責管理數據和業務邏輯(也就是承接state和dispatch)
React-Redux提供一個connect方法能夠把組件和store連接起來,把state、dispatch方法都放入到當前組件上,同時是state變成響應式。其實就是一個高階組件包裝了一下我們寫的UI組件。
// index.js
import React from 'react';
import ReactDom from "react-dom"
//引入Provider
import {Provider} from "react-redux";
import Counter from "./counter/ReactReduxCounter";
import store from "./redux/basic/store"
ReactDom.render(
<Provider store={store}>
<Counter></Counter>
</Provider>,
document.getElementById("root")
)
我們看到通過用<Provider/>
來包裝我們真正的reactDOM,就可以讓里面所有的子組件都使用store了。
注意:用到store的子組件應該是通過connect包裝后的。
// Counter.js
import React, { Component } from 'react';
import {connect} from "react-redux"
//子組件
class Counter extends Component {
//方法調用后自動更新數據
increment=()=>{
this.props.increment()
}
decrement=()=>{
this.props.decrement()
}
render() {
return (
<div>
<div>{this.props.num}</div>
<button onClick={this.increment}>點我+1</button>
<button onClick={this.decrement}>點我-1</button>
</div>
);
}
}
//該函數作為connect的第一個參數,能拿到state
//映射state到組建的props上
function mapStateToProps(state){
return {
num:state.counter
}
}
//該函數作為connect的第二個參數,能拿到dispatch
//映射dispatch方法到組建的props上
function mapDispatchToProps(dispatch){
return {
increment(){
dispatch({
type:"increment"
})
},
decrement(){
dispatch({
type:"decrement"
})
}
}
}
//connet函數執行返回一個高階組件
//調用這個高階組件,傳入當前組件Counter作為參數,返回一個增強的Counter組件
//這個增強的組件props里有store的state和dispach方法
export default connect( mapStateToProps , mapDispatchToProps )(Counter)
/// store.js
import { createStore } from "redux"
const defaultState={
counter:0
}
//純函數
let reducers =(state = defaultState ,action)=>{
switch (action.type){
case "increment":
console.log("increment")
return {
counter:state.counter+1
}
case "decrement":
return {
counter:state.counter-1
}
default :
return state
}
}
const store = createStore(reducers)
export default store
3. mapStateToProps()和mapDispatchToProps()
注意到,上述connect()中傳遞了兩個參數:mapStateToProps()和mapDispatchToProps()。
通過觀察connect源碼(下一節)發現,他是利用了context
封裝了一個高階組件,在封裝過程中,會調用上述兩個方法,把store中的state和dispatch取出來放到子組件的props里。這樣我們在編寫組件的時候,就可以無感知的調用state和dispatch了。
-
mapStateToProps
-
他是一個函數,接受state作為參數,返回一個對象,這個對象是你想從state中取出的數據
-
const mapStateToProps = (state) => { return { counter: state.counter } }
-
-
maoDispatchToProps
-
他是一個函數,接受dispatch和ownProps(容器組件的props)兩個對象,返回一個對象,這個對象中的值應該是一個包含可以觸發dispatch的方法。
-
const mapDispatchToProps = (dispatch, ownProps) => { return { increment(num) { dispatch({ type: "increment", payload: num }) } } }
-
4. connect高階組件的大致實現
function connect(mapStateToProps, mapDispatchToProps) {
return function(WrappedComponent) {
class Connect extends React.Component {
componentDidMount() {
// 組件加載完成后訂閱store變化,如果store有變化則更新UI
this.unsubscribe = this.context.store.subscribe(this.handleStoreChange.bind(this));
}
componentWillUnmount() {
// 組件銷毀后,取消訂閱事件
this.unsubscribe();
}
handleStoreChange() {
// 更新UI
this.forceUpdate();
}
render() {
return (
<WrappedComponent
{...this.props}
{...mapStateToProps(this.context.store.getState())} // 參數是store里面的數據
{...mapDispatchToProps(this.context.store.dispatch)} // 參數是store.dispatch
/>
);
}
}
Connect.contextTypes = {
store: PropTypes.object
};
return Connect;
};
}
5. React-Redux的hook版
可能我們覺得每次都要用connect封裝一下組件太麻煩了,還要寫mapStateToProps和mapDispatchToProps,react-redux為我們提供了一些hooks,比如常用的useSelector和useDispatch。
- useSelector接收兩個參數:selector和equalityFn。第一個參數是一個函數以獲取你想要的state,第二個可選參數也是個函數,用于比較狀態是否發生改變,如果函數執行結果為false就表示狀態發生改變,組件將重新渲染。
- useDispatch直接調用可獲得dispatch
import { useSelector, useDispatch } from 'react-redux'
const Home = (props) => {
const dispatch = useDispatch();
const number = useSelector(state => state.number);
const handleClick = () => {
dispatch({
type: 'ADD',
payload: 1
})
}
return (
<h2>{number}</h2>
<button onClick={handleClick}/>
)
}
6. Redux和React-Redux的關系
依初初初學者短見,我們想要在React項目中使用共享數據時,使用的store都是通過Redux提供的createStore創建的store。React-Redux只是幫我們封裝了一些方法,以幫助我們在子組件中更便利的使用state和dispatch等。
三. Rematch
如果說React-Redux是在Redux的基礎上幫助子組件更便捷的使用state和dispatch。
那Rematch則是在和React-Redux搭配基礎上幫我們更具封裝性的創建store,加上更便利的使用state和dispatch。
1. 創建store
之前我們是通過redux的createStore的方法來創建store。
import { createStore } from 'redux';
const store = createStore(reducer, initState);
如果還想要添加一些異步處理,那可能還要加入中間件,reducer也變得很復雜。
而Rematch提供了init可以讓我們把initState、reducers、異步處理方法封裝在一起。
// store.js
import { init } from '@rematch/core';
const count = {
state: 0,
reducers: {
increment: (state, payload) => state + payload,
decrement: (state, payload) => state - payload
},
effects: { // 帶有異步處理數據的reducer
async incrementAsync(payload) {
await func();
this.increment(payload);
}
}
}
export const store = init(count);
如果到此為止,你可以用上述生成的store正常的配合react-redux的provider、connect去使用他。
2. 模塊化
rematch建議我們將數據分類保存到models文件夾下的不同的文件里。最后在models/index.js中統一導出。
例如:
- models/count.js
- Models/user.js
// models/index.js
export { default as count } from './count'
export { default as user } from './user'
// store.js
import { init } from '@rematch/core'
import * as models from './models'
export const store = init({models})
于是我們在每個組件上,可以直接引入store來取到想要的某個模塊中的store和dispatch
// counter.js
import { store } from './store.js'
const counter = () => {
const { counter, user } = store.getState();
const add = store.dispatch.counter.add;
const changName = store.dispatch.user.changeName;
const handleClick = () => {
add({
type: 'ADD',
payload: 1
})
}
return (
<h2>{counter.number}</h2>
<h2>{user.name}</h2>
<button onClick={handleClick}/>
)
}
但注意,以上state不是響應式的,也就是說觸發dispatch改變state并不會讓視圖更新,我們依然需要結合react-redux中的connect或者hook來讓其變成響應式。
原文鏈接:https://blog.csdn.net/Pinoochio/article/details/122115950
相關推薦
- 2022-05-02 Python的os包與os.path模塊的用法詳情_python
- 2023-03-05 Python協程的四種實現方式總結_python
- 2022-05-12 uni-app混合原生安卓開發
- 2022-04-27 幾個關于python??Pdf?技巧的分享_python
- 2022-03-20 C語言數據結構之鏈隊列的基本操作_C 語言
- 2022-07-03 el-form表單新增表單項動態校驗;el-form校驗動態表單v-if不生效;
- 2022-12-04 pyecharts?X軸標簽太長被截斷的問題及解決_python
- 2022-05-11 hive 安裝步驟以及問題解決方案
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支