React,Vue Gadget for Managing Asynchronous Load Status

Keywords: Javascript React github Vue

load-state

github

Common projects often encounter management that requires a loading state, such as form submission, which is typically written like this

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("Succeed");
    } catch (e) {
      this.setState({ loading: false });
    }
  };

  render() {
    return (
      <Spin spinning={this.state.loading}>
        <Button onClick={this.submit}>Submit</Button>
      </Spin>
    );
  }
}

Writing in this way can sometimes feel tedious, and many people don't write exception handling. The effect of loading can't be eliminated when an exception occurs, which makes it impossible for users to operate and easy for them to forget to turn off the effect of loading under some branch of circumstances.

Use load-state to write like this

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("Succeed");
  };

  render() {
    return (
      <Spin spinning={!!this.state.loading}>
        <Button onClick={this.submit}>Submit</Button>
      </Spin>
    );
  }
}

Is it refreshing, don't need to manage the loading state yourself, don't forget to turn off the loading state, if you need exception handling as usual, don't affect

Another problem is that sometimes a loading state is used in multiple places at the same time (changing values), which can easily lead to confusion. load-state uses a number to manage loading states.
It's okay to use one state in multiple places at the same time.
Note here <Spin spinning={!! This.state.loading}>is used!! To convert types

For a more elegant use, of course, you can refer to the implementation of React Hook below, which allows you to use without converting types

loadState.createFn(changeLoadFn)

Create a loading method

parameter

The changeLoadFn function(change) state change method, change 1 or -1, can get the next state value through the loadState.getNextState method

Return value

Returns a load method function(promise),

  • Promise can be a Promise object or a method that returns a Promise object
  • Projects above version 0.1 can be boolean values, true loading counts + 1, false-1 (this requires managing the state yourself, otherwise it may cause some exceptions)
  • Executing this method will result in a managed state value of + 1, which will be -1 if the promise object succeeds or has an exception

loadState.getNextState(cur, change)

Get the next status value

parameter

  • cur: Current status value
  • change: Modify parameters accepted in the value 1 or -1 loadState.createFn parameter method

Return value

If cur is boolean or empty consider 0 and continue with change

Use encapsulation within the loadState.createRFn(field) React component

parameter

State name in field state

Return value

Return a loading method

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);
    });
    // Asynchronous return values are acceptable
    const result = await this.loading(pm);
    console.log(result);

    // As with the above code, it's much more comfortable to use await
    // this.loading(pm).then(res => {
    //   console.log(res); // 123
    // });

    // A parameter can also be a method to return promise
    // const result = this.loading(async () => {
    //   return await fetch("/aa/bb");
    // });
  };

  render() {
    return (
      <div>
        <Spin spinning={!!this.state.data.loading}>
          <Alert
            message="Click the button to enter loading state"
            description="Multiple clicks."
            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>
    );
  }
}

Demo address: codesandbox

react hook wrapping example

hook works better

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 Example
 */

/**
 * loading State Management hook
 * @param initValue Nullable, initial, boolean or int
 * @param isNum Whether to return the loading state as a number, defaulting to false, returning the boolean state
 * @return {[Current loading status value, state management method]}
 */
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() {
  // Getting the loading state and the loading state management method
  const [loading, loadingFn] = useLoading(false, true);

  const showSetTimeout = async ms => {
    const pm = new Promise((resolve, reject) => {
      setTimeout(() => resolve(123), ms);
    });
    // Asynchronous return values are acceptable
    const result = await loadingFn(pm);
    console.log(result);
  };
  return (
    <div>
      <Spin spinning={!!loading}>
        <Alert
          message="Click the button to enter loading state"
          description="Multiple clicks."
          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>
  );
}

Demo address: codesandbox

Use encapsulation within the loadState.createVFn(field) Vue component

parameter

State name in field data

Return value

Return a loading method

<template>
  <div>
    <el-alert v-loading="!!data.loading" title="Click the button to enter loading state" type="success" description="Multiple clicks"></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);
      });
      // Asynchronous return values are acceptable
      const result = await this.loadingFn(pm);
      console.log(result);

      // As with the above code, it's much more comfortable to use await
      // this.loading(pm).then(res => {
      //   console.log(res); // 123
      // });

      // A parameter can also be a method to return promise
      // const result = this.loading(async () => {
      //   return await fetch("/aa/bb");
      // });
    }
  }
};
</script>

Demo address: codesandbox

Posted by DangerousDave86 on Tue, 03 Sep 2019 15:17:59 -0700