react-subscribe

1.3.2 • Public • Published

Subscribe event in react style

Feel tired about componentDidMount and componentWillUnmount pair to subscribe and unsubscribe events? This module will help.

Feel tired about componentDidMount and componentWillReceiveProps and write a fetchData method and deal with reentry issue and 'warning: setState after componentDidUnmount''? This module will help.

Helpful for both react and react-native.

Installation

Just npm it:

npm install react-subscribe --save

API

Subscribe

Use together with fbemitter like event emitters. Emitter should have a addListener method, which return a subscription object. Call remove method of subscription object will do unsubscribe.

Now you can subscribe event like this:

import {Subscribe} from 'react-subscribe';
import someEmitter from '../someModule';
 
class MyCleanComponent extends React.Component {
  state = {
    listening = true,
 
    target = someEmitter,
    eventName = 'eventName',
  }
  onEvent = ev => {
  };
  render() {
    return (
      <div>
        {/* This will render nothing: */}
        <Subscribe target={someEmitter} eventName="eventName" listener={this.onEvent} />
 
        {/* You can subscribe many event here. */}
        <Subscribe target={someEmitter} eventName="otherEvent" listener={this.onEvent} />
 
        {/* You can subscribe with a condition. */}
        {/* It will subscribe/unsubscribe when condition changes and this component re-renders. */}
        {this.state.listening && <Subscribe target={someEmitter} eventName="eventName" listener={this.onEvent} />}
 
        {/* You can use expression for target & eventName and change it after re-render.*/}
        {/* This will safely unsubscribe old target/eventName and resubscribe the new one(s).*/}
        <Subscribe target={this.state.target} eventName={this.state.eventName} listener={this.onEvent} />
      </div>
    );
  }
}

Subscribe will automatic begin when after component mount and automatic unsubscribe before component unmount.

In the case when your component renders a component that should not have any children or have special meaning with children (like TabBar from react-native), you should use Subscribe in this form:

class MyCleanComponent extends React.Component {
  onEvent = ev => {
  };
  render() {
    // This component will render only a <input />
    return (
      <Subscribe target={someEmitter}, eventName="eventName" listener={this.onEvent}>
          <input />
      </Subscribe>
    );
  }
}

If you use react 16+, you also can use React.Fragment instead:

class MyCleanComponent extends React.Component {
  onEvent = ev => {
  };
  render() {
    // This component will render only a <input />
    return (
      <React.Fragment>
        <input />
        <Subscribe target={someEmitter}, eventName="eventName" listener={this.onEvent} />
      </React.Fragment>
    );
  }
}

SubscribeDOM

Use SubscribeDOM instead of Subscribe when subscribing a DOM Event.

class MyCleanComponent extends React.Component {
  onBodyWheel = ev => {
    // Move indicates when document scrolls.
    const val = document.body.scrollTop / document.body.offsetHeight;
    this.refs.active.style.top = val * 20 + 'px';
  };
  return() {
    return (
      <div>
        {/* Other body components */}
        <SubscribeDOM
          target={document}
          eventName="scroll"
          listener={this.onBodyWheel}
        />
        <div ref="active" className={classnames(styles.dot, styles.active)} />
      </div>
    );
  }
}

Timer

Use Timer instead of setInterval and clearInterval.

import { Timer } from 'react-subscribe';
 
class CooldownButton extends React.Component {
  state = {
    cd: 60,
  };
  onTimer = () => {
    this.setState({ cd: this.state.cd - 1 });
  };
  return() {
    if (this.state.cd <= 0) {
      return <div>Cooldown is over and onTimer will not be called again!</div>;
    }
    return (
      <div>
        <Timer interval={1000} onTimer={this.onTimer} />
        There is still {this.state.cd} seconds to go.
      </div>
    );
  }
}

Note: If you change interval value, the timer will be reset.

eg: You have a timer which interval is 60 seconds, and you click a button after 30 second which changes interval into 40 seconds, The next event will be fired 40 seconds later (totally 70 seconds after your component mounted) which may let user feel weired.

Fetch

Use Fetch instead of fetch or promise. Fetch will auto reload when url or type changes.

function SomeComponent({ data, loading, error, reload, statusCode }) {
  if (loading) {
    return <div>loading...</div>;
  }
  if (error) {
    return (
      <div>
        Error: {error.message}{' '}
        <a href="#" onClick={reload}>
          Reload
        </a>
      </div>
    );
  }
  return (
    <div>
      <p>Status Code: {statusCode}</p>
      <p>{JSON.stringify(data)}</p>
    </div>
  );
}
 
const FETCH_OPTION = {
  method: 'POST',
  headers: {
    'X-AccessToken': 'some_token',
  },
  credentials: 'include', // Default credentials is 'same-origin' in `Fetch`
};
 
export default function SomePage(props) {
  const { id } = props;
  return (
    <div>
      <Fetch url={`/some/api/${id}`} type="json" option={FETCH_OPTION}>
        <SomeComponent />
      </Fetch>
    </div>
  );
}

Use function as children if you don't want to seperate component:

export default function SomePage(props) {
  const { id } = props;
  return (
    <div>
      <Fetch url={`/some/api/${id}`} type="json" option={FETCH_OPTION}>
        {({ data, loading, error, reload, statusCode }) => {
          return <div>{data && <p>{JSON.stringify(data)}</p>}</div>;
        }}
      </Fetch>
    </div>
  );
}

Use 'doFetch' props to provide a custom async function.

async function customRequest(url) {
  return 'Hello, world!';
}
 
export default function SomePage(props) {
  return (
    <div>
      <Fetch doFetch={customRequest} url="foo">
        <SomeComponent />
      </Fetch>
    </div>
  );
}

Maybe you want use Axios instead of fetch, I suggest you to use axios status/error management:

function SomeComponent({ data, loading, error, reload }) {
  if (loading) {
    return <div>loading...</div>;
  }
  if (error) {
    return (
      <div>
        <p>Error: {error.message} </p>
        <p>StatusCode: {error.response.status}</p>
        <p>{JSON.stringify(error.response.data)}</p>
        <a href="#" onClick={reload}>
          Reload
        </a>
      </div>
    );
  }
  return (
    <div>
      <p>Status Code: {data.status}</p>
      <p>{JSON.stringify(data.data)}</p>
    </div>
  );
}
 
export default class SomePage extends React.Component {
  render() {
    <div>
      <Fetch doFetch={Axios.get} url="foo">
        <SomeComponent />
      </Fetch>
    </div>;
  }
}

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 1.3.2
    3
    • latest

Version History

Package Sidebar

Install

npm i react-subscribe

Weekly Downloads

4

Version

1.3.2

License

ISC

Unpacked Size

72.3 kB

Total Files

9

Last publish

Collaborators

  • tdzl2003