Skip to content

Using with React

DigitalBrainJS edited this page Oct 17, 2021 · 1 revision

Using with React

React class components with CPromise decorators

With CPromise decorators, a generic React class component that fetches JSON might look like the following:

Live Demo

import React, { Component } from "react";
import {
  CPromise,
  CanceledError,
  ReactComponent,
  E_REASON_UNMOUNTED,
  listen,
  cancel
} from "c-promise2";
import cpAxios from "cp-axios";

@ReactComponent
class TestComponent extends Component {
  state = {
    text: ""
  };

  *componentDidMount(scope) {
    console.log("mount", scope);
    scope.onCancel((err) => console.log(`Cancel: ${err}`));
    yield CPromise.delay(3000);
  }

  @listen
  *fetch() {
    this.setState({ text: "fetching..." });
    try {
      const response = yield cpAxios(this.props.url).timeout(
        this.props.timeout
      );
      this.setState({ text: JSON.stringify(response.data, null, 2) });
    } catch (err) {
      CanceledError.rethrow(err, E_REASON_UNMOUNTED);
      this.setState({ text: err.toString() });
    }
  }

  *componentWillUnmount() {
    console.log("unmount");
  }

  render() {
    return (
      <div className="component">
        <div className="caption">useAsyncEffect demo:</div>
        <div>{this.state.text}</div>
        <button
          className="btn btn-success"
          type="submit"
          onClick={() => this.fetch(Math.round(Math.random() * 200))}
        >
          Fetch random character info
        </button>
        <button
          className="btn btn-warning"
          onClick={() => cancel.call(this, "oops!")}
        >
          Cancel request
        </button>
      </div>
    );
  }
}

Using some specific decorators we can control our async flow in a declarative way: Live Demo

import React from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import "./styles.css";
import { async, listen, cancel, timeout } from "c-promise2";
import cpFetch from "cp-fetch";

export class TestComponent extends React.Component {
  state = {
    text: ""
  };

  @timeout(5000)
  @listen
  @async
  *componentDidMount() {
    console.log("mounted");
    const response = yield cpFetch(this.props.url);
    this.setState({ text: `json: ${yield response.text()}` });
  }

  render() {
    return <div>{this.state.text}</div>;
  }

  @cancel()
  componentWillUnmount() {
    console.log("unmounted");
  }
}

It automatically manages async code i.g request, so it protects from warning appearing like:

Warning: Can’t perform a React state update on an unmounted component.

React functional components

If you prefer functional components you can use use-async-effect2 package that decorates CPromise into custom React hooks - useAsyncEffect and useAsyncCallback:

Live demo

import React from "react";
import {useState} from "react";
import {useAsyncEffect} from "use-async-effect2";
import cpFetch from "cp-fetch";

function FetchComponent(props) {
    const [text, setText] = useState("");

    const cancel= useAsyncEffect(function* () {
            setText("fetching..."); 
            const response = yield cpFetch(props.url); // will throw a CanceledError if component get unmounted
            const json = yield response.json();
            setText(`Success: ${JSON.stringify(json)}`);
    }, [props.url]);

    return (
      <div>
      <span>{text}</span>
      <button onClick={cancel}>Cancel request</button>
      </div>
    );
}