React,Vue管理異步加載狀態的小工具

load-state

github

平常項目中經常碰到需要有加載中狀態的管理,比如表單提交,一般是這樣寫的

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章