load-state
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