load-state
平常項目中經常碰到需要有加載中狀態的管理,比如表單提交,一般是這樣寫的
import React from "react";
import "antd/dist/antd.css";
import { Spin, Button } from "antd";
class Demo extends React.Component {
state = { loading: false };
submit = async data => {
this.setState({ loading: true });
try {
let res = await fetch("xxx.com", data);
this.setState({ loading: false });
alert("成功了");
} catch (e) {
this.setState({ loading: false });
}
};
render() {
return (
<Spin spinning={this.state.loading}>
<Button onClick={this.submit}>提交</Button>
</Spin>
);
}
}
這樣寫很多時候會感覺比較繁瑣,而且很多人大多數都不寫異常處理,可能出現異常的時候加載中效果無法消除,導致用戶無法操作,也很容易忘記再某些情況分支下關閉加載中效果
使用load-state這個小東西就可以這樣寫啦
import React from "react";
import "antd/dist/antd.css";
import { Spin, Button } from "antd";
import loadState from "load-state";
class Demo extends React.Component {
state = { loading: false };
loading = loadState.createRFn("loading");
submit = async data => {
let res = await this.loading(fetch("xxx.com", data));
alert("成功了");
};
render() {
return (
<Spin spinning={!!this.state.loading}>
<Button onClick={this.submit}>提交</Button>
</Spin>
);
}
}
是不是清爽很多,不用自己去管理loading狀態,不會忘記去關閉loading狀態,如果有需要異常處理和平常一樣的操作,不影響
還有一個問題就是有時候一個loading狀態,多個地方同時使用(改變值),很容易出現混亂,load-state是使用一個數字來管理加載狀態的,
多個地方同時使用一個狀態也不會有問題,
注意看這裏<Spin spinning={!!this.state.loading}>
使用了 !!
來轉換類型
當然想更優雅的使用可以參考下方React Hook的實現,使用可以不轉換類型
loadState.createFn(changeLoadFn)
創建一個加載方法
參數
changeLoadFn
function(change)
狀態變更方法,change爲1或-1, 可通過loadState.getNextState
方法獲取下一個狀態值
返回值
返回一個加載方法function(promise)
,
- promise可以是一個
Promise對象
或返回Promise對象的方法
-
0.1版本以上
promise可以是boolean值,true loading 計數+1 , false -1(這樣使用需要自己管理好狀態,不然可能會導致一些異常)
- 執行此方法,方法會將管理的狀態值+1,promise對象成功或出現異常時會將狀態值-1
loadState.getNextState(cur, change)
獲取下一個狀態值
參數
- cur: 當前狀態值
- change: 修改值 1 或 -1
loadState.createFn
參數方法中接受到的參數
返回值
如果cur
爲boolean類型或爲空則認爲0,再繼續加上change
loadState.createRFn(field) React組件內使用封裝
參數
field
state中的狀態名稱
返回值
返回一個加載方法
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import { Spin, Alert, Button } from "antd";
import loadState from "load-state";
class Card extends React.Component {
state = { data: { loading: false } };
loading = loadState.createRFn("data.loading");
showSetTimeout = async ms => {
const pm = new Promise((resolve, reject) => {
setTimeout(() => resolve(123), ms);
});
// 可以接受到異步返回值
const result = await this.loading(pm);
console.log(result);
// 和上面代碼一樣 用await舒服很多
// this.loading(pm).then(res => {
// console.log(res); // 123
// });
// 參數也可以是一個返回promise的方法
// const result = this.loading(async () => {
// return await fetch("/aa/bb");
// });
};
render() {
return (
<div>
<Spin spinning={!!this.state.data.loading}>
<Alert
message="點擊按鈕進入loading狀態"
description="可多次點擊."
type="info"
/>
</Spin>
<div style={{ marginTop: 16 }}>
Loading state: {this.state.data.loading} -{" "}
{(!!this.state.data.loading).toString()}
<Button
style={{ margin: 16 }}
type="primary"
onClick={() => this.showSetTimeout(1000)}
>
1000ms
</Button>
<Button type="primary" onClick={() => this.showSetTimeout(2000)}>
2000ms
</Button>
</div>
</div>
);
}
}
演示地址:codesandbox
react hook封裝示例
hook用起來更爽
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import { Spin, Alert, Button } from "antd";
import loadState from "load-state";
/**
* react hook 示例
*/
/**
* loading狀態管理hook
* @param initValue 可空, 初始值, 可以爲boolean值或int
* @param isNum 是否返回數字形式的加載狀態, 默認爲false, 返回boolean形式的狀態
* @return {[當前loading狀態值, 狀態管理方法]}
*/
function useLoading(initValue, isNum = false) {
const [loading, setLoading] = useState(initValue);
return [
isNum ? loading : !!loading,
loadState.createFn(change => {
setLoading(prev => loadState.getNextState(prev, change));
})
];
}
function Demo() {
// 獲取loading 狀態 和 loading狀態管理方法
const [loading, loadingFn] = useLoading(false, true);
const showSetTimeout = async ms => {
const pm = new Promise((resolve, reject) => {
setTimeout(() => resolve(123), ms);
});
// 可以接受到異步返回值
const result = await loadingFn(pm);
console.log(result);
};
return (
<div>
<Spin spinning={!!loading}>
<Alert
message="點擊按鈕進入loading狀態"
description="可多次點擊."
type="info"
/>
</Spin>
<div style={{ marginTop: 16 }}>
Loading state: {loading} - {(!!loading).toString()}
<Button
style={{ margin: 16 }}
type="primary"
onClick={() => showSetTimeout(1000)}
>
1000ms
</Button>
<Button type="primary" onClick={() => showSetTimeout(2000)}>
2000ms
</Button>
</div>
</div>
);
}
演示地址:codesandbox
loadState.createVFn(field) Vue組件內使用封裝
參數
field
data中的狀態名稱
返回值
返回一個加載方法
<template>
<div>
<el-alert v-loading="!!data.loading" title="點擊按鈕進入loading狀態" type="success" description="可多次點擊"></el-alert>
<p>{{data.loading}}-{{!!data.loading}}</p>
<el-button type="primary" @click="showSetTimeout(1000)">1000ms</el-button>
<el-button type="primary" @click="showSetTimeout(2000)">2000ms</el-button>
</div>
</template>
<script>
import loadState from "load-state";
export default {
data() {
return {
tableData: [],
data: {
loading: false
}
};
},
methods: {
loadingFn: loadState.createVFn("data.loading"),
showSetTimeout: async function(ms) {
const pm = new Promise((resolve, reject) => {
setTimeout(() => resolve(123), ms);
});
// 可以接受到異步返回值
const result = await this.loadingFn(pm);
console.log(result);
// 和上面代碼一樣 用await舒服很多
// this.loading(pm).then(res => {
// console.log(res); // 123
// });
// 參數也可以是一個返回promise的方法
// const result = this.loading(async () => {
// return await fetch("/aa/bb");
// });
}
}
};
</script>
演示地址:codesandbox