npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-cofx

v1.0.11

Published

Make async calls when rendering a component.

Downloads

2

Readme

react-cofx Build Status

Make async calls when rendering a component.

Motivation

Some components need to request data but don't require being stored in redux. For example, if the data is only being used for that one component, it is not necessary to place it into redux. However, leveraging lifecycle methods like componentDidMount and setState has its own set of problems because of the way react handles mounting. For example, an unmounted component cannot call setState so if an async request comes back when the component gets unmounted then that will result in an error. People get around that issue by adding a class property this._mounted and then setting it to false when unmounting. This adds quite a bit of complexity to a component and some argue is an anti-pattern. There are also cases where the component will not unmount when you expect it to and the async function will not be activated.

This library attempts to create a simple API for calling an async function and returning some data to the component. This separates data fetching from the actual component and uses roughly the same API that react-redux and redux-saga uses to keep things easy to test and migrate to a redux container component.

Features

  • Separates data fetching from component
  • Connects a component to an async function
  • Caches response of async function
  • Async function has access to component's props
  • Component has ability to refetch data
  • Same structure as connecting a component to redux for easy migration
  • Async function can be anything that co can convert to a promise
  • Leverages cofx which handles side effects as data
  • Testing is simple and familiar if previously used redux and redux-saga
  • Upgradability to redux-cofx and redux-saga

Upgrade plan

Sometimes all we need to do is fetch data from a server and load it into one component. That data lives inside that component and we don't need to share it with anything else. This is where react-cofx shines. However, because requirements change over time, we need to eventually share that data with multiple components and now we need to save the data in redux. We could use redux-thunk to accomplish that goal, but it's difficult to test thunks and the way to fetch the data looks a little different than react-cofx. This is where redux-cofx shines. The function we wrote for react-cofx looks almost identical to the one we use for redux-cofx. The only addition is redux-cofx provides the ability to query redux state and dispatch actions. Sometimes requirements change even more and now we need the ability to listen to multiple events and other more complex flow control mechanisms. This is when we would introduce redux-saga. The cofx API was built with redux-saga in mind. It's the same exact API. All we would need to do is change the import path for call, etc. to redux-saga/effects and it should work exactly the same.

So what do we have? We have an upgrade path to start small (react-cofx), upgrade to redux with simple side-effects (redux-cofx), and then upgrade to really complex flow mechanisms (redux-saga).

react-cofx -> redux-cofx -> redux-saga

Usage

yarn add react-cofx
import createFetcher from "react-cofx";

const fetch = window.fetch;

async function fetchMovies() {
  const resp = await fetch("http://httpbin.org/get?movies=one,two,three");
  const json = await resp.json();
  const movies = json.args.movies.split(",");

  return movies;
}

const DisplayMovies = ({ data = [] }) => (
  <div>
    {data.map(movie => (
      <div key={movie}>{movie}</div>
    ))}
  </div>
);

const movieFetcher = createFetcher(fetchMovies);
const DisplayMoviesContainer = movieFetcher()(DisplayMovies);

const App = () => (
  <div>
    <DisplayMoviesContainer />
  </div>
);

The async function could be anything that co supports. A generator, a promise, an array of promises, an object of promises, even a normal function. react-cofx will take the result of what you pass it and send it to the component.

Here at ReactFetcher, Inc. we like to use cofx which treats side effects as data with an API similar to redux-saga:

import createFetcher from "react-cofx";
import { call } from "cofx";

const fetch = window.fetch;

function* fetchMovies() {
  const resp = yield call(fetch, "http://httpbin.org/get?movies=one,two,three");
  const json = yield call([resp, "json"]);
  const movies = json.args.movies.split(",");

  return movies;
}

Using cofx makes testing side effects simple.

Want to change the way the data gets sent to the component? Use mapStateToProps

import createFetcher from "react-cofx";

const fetch = window.fetch;

async function fetchMovies() {
  const resp = await fetch("http://httpbin.org/get?movies=one,two,three");
  const json = await resp.json();
  const movies = json.args.movies.split(",");

  return movies;
}

const DisplayMovies = ({ data = [] }) => (
  <div>
    {movies.map(movie => (
      <div key={movie}>{movie}</div>
    ))}
  </div>
);

const movieFetcher = createFetcher(fetchMovies);
const mapStateToProps = movies => ({ movies }); // default mapStateToProps: (data, error) => ({ data, error });
const DisplayMoviesContainer = movieFetcher(mapStateToProps)(DisplayMovies);

const App = () => (
  <div>
    <DisplayMoviesContainer />
  </div>
);

Want to refetch data? Like mapDispatchToProps in redux, we have mapRefetchToProps which will bust the cache and call the request again.

const DisplayMovies = ({ movies = [], refetch }) => {
  return h("div", [
    h(
      "a",
      {
        href: "#",
        onClick: () => {
          refetch();
        }
      },
      "refetch"
    ),
    h("div", movies.map(movie => h("div", { key: movie }, movie)))
  ]);
};

const movieFetcher = createFetcher(fetchMovies);
const mapStateToProps = movies => ({ movies });
const mapRefetchToProps = refetch => ({ refetch });
const DisplayMoviesContainer = movieFetcher(mapStateToProps, mapRefetchToProps)(
  DisplayMovies
);

Async function also receives props sent to component

function* fetchMovies({ movieName }) {
  const resp = yield call(fetch, `http://httpbin.org/get?movies=${movieName}`);
  const json = yield call([resp, "json"]);
  const movies = json.args.movies.split(",");

  return movies;
}

const DisplayMovies = ({ movies = [] }) => (
  <div>
    {movies.map(movie => (
      <div key={movie}>{movie}</div>
    ))}
  </div>
);

const movieFetcher = createFetcher(fetchMovies);
const mapStateToProps = movies => ({ movies });
const DisplayMoviesContainer = movieFetcher(mapStateToProps)(DisplayMovies);

const App = () => (
  <div>
    <DisplayMoviesContainer movieName="Transporter" />
  </div>
);

Async function returns an error? mapStateToProps has a second parameter for any error states that are returned from the async function

function* fetchMovies({ movieName }) {
  throw new Error("Something bad happened");
}

const DisplayMovies = ({ movies = [], error }) => {
  if (error) {
    return <div>{error.message}</div>;
  }

  return (
    <div>
      {movies.map(movie => (
        <div key={movie}>{movie}</div>
      ))}
    </div>
  );
};

const movieFetcher = createFetcher(fetchMovies);
const mapStateToProps = (movies, error) => ({
  movies: movies || [],
  error
});
const DisplayMoviesContainer = movieFetcher(mapStateToProps)(DisplayMovies);

const App = () => (
  <div>
    <DisplayMoviesContainer movieName="Transporter" />
  </div>
);

Want a loader?

const Loader = () => <div>LOADING!</div>;
const DisplayMoviesContainer = movieFetcher(mapStateToProps)(
  DisplayMovies,
  Loader
);