Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partial failures in fetch #346

Open
idolizesc opened this issue Jun 19, 2015 · 9 comments
Open

Partial failures in fetch #346

idolizesc opened this issue Jun 19, 2015 · 9 comments

Comments

@idolizesc
Copy link

The fetch API has a failed state, but if you are using the Marty containers then there is no way to get the parts of the fetch that succeeded on a failed fetch (like there is with pending). This would be very useful if you are fetching several items for one page, passing them down as props, and you rely on part of the data even if some other part has failed.

Perhaps adding the fetch result to the callback here as an optional second parameter (besides just error) would provide a workable API?

I assume current limitation comes from the behavior of getFetchResult; it looks like as soon as there is any fetch result in error it does not add any results, and I'm also not sure if the container would wait for other fetches if one goes into error right away.

How hard would this be to add?

@taion
Copy link
Member

taion commented Jun 19, 2015

I wonder if this isn't more a limitation of the API. An alternative way you could do this would be to, in Store#fetch, specify what you want the returned value to be during pending and failed states, and only invoke the container's pending or failed handler if not all fetches have those values specified.

@idolizesc
Copy link
Author

@taion I don't follow what you mean here. Are you suggesting having some default results in the store?

To clarify: the Marty container components wrap 1-N fetch results with one single fetch result. The case where at least one of the fetches fails but at least one succeeds is difficult to handle, because there is no easy way to access the data returned by the successful fetch via the Marty container's failed() render function.

@taion
Copy link
Member

taion commented Jun 19, 2015

Instead of just having

store.fetch({id, locally, remotely});

also support

store.fetch({id, locally, remotely, pending, failed});

And pass those through as "done" values to the container when in those states.

@idolizesc
Copy link
Author

As an aside, the only possible workaround I can see with the current implementation is to use multiple Marty container components wrapping each other, which is very ugly and hard to reason about.

@idolizesc
Copy link
Author

@taion I see. I do not think that is necessary though because the existing semantics of an individual fetch already work - I believe the container's wrapping of these fetches and the behavior of that is where the problem lies (not passing back partial results in a failure and not waiting for all results before deeming the entire thing failed).

@taion
Copy link
Member

taion commented Jun 19, 2015

The problem is that in the general case, you might have some mix of done, pending, and failed fetches for a container with multiple fetches. I don't know that it's necessarily that expressive or manageable to give you the ability to sort all of those out on the container API itself - it might be better just to pass user-configurable values down to the wrapped component to robustly handle error cases.

That's effectively what the pending example already does - I'm just saying to put the logic to treat the pending value of user on the fetch call in getUser.

@taion
Copy link
Member

taion commented Jun 19, 2015

In the example there you might have something like

// UserStore.js
class UserStore extends Store {
  getUser(id) {
    return this.fetch({
      id,
      locally: () => this.state.users[id],
      remotely: () => this.app.userQueries.getUser(id)
    });
  }
}

// UserComponent.js
createContainer(UserComponent, {
  listenTo: 'userStore',
  fetch: {
    user() {
      return this.app.userStore.getUser(this.props.id);
    }
  },
  pending() {
    return this.done({
      user: {}
    });
  }
});

but I'd rather express this as

// UserStore.js
class UserStore extends Store {
  getUser(id) {
    return this.fetch({
      id,
      locally: () => this.state.users[id],
      remotely: () => this.app.userQueries.getUser(id),
      pending: () => Object()
    });
  }
}

// UserComponent.js
createContainer(UserComponent, {
  listenTo: 'userStore',
  fetch: {
    user() {
      return this.app.userStore.getUser(this.props.id);
    }
  }
});

In the case where you want detailed per-fetch error/pending handling, it seems like it's just better just to shove that down to the component anyway.

The current behavior of just rendering an empty div and allowing to render some sort of message in the case of any failure is fine, but it seems like an anti-pattern to try to do much more than that in the container.

Ultimately you're most likely just trying to resolve the missing values in some appropriate way and pass them down to the wrapped component, so you might as well specify those fetch-by-fetch.

@idolize
Copy link

idolize commented Jun 26, 2015

@taion I get what you are saying now. I think it definitely would be worth considering your option, although I do kind of like the ability to be explicit about failure/loading cases inside my views without having to clutter up my Store with that kind of information (or at least having that option). The current implementation also avoids having to return/check for sentinel values like empty objects (which is one of the things I don't really like about other implementations such as alt).

Maybe there could be several fetch implementations out there, given that the fetchResult type is exposed, which we could pick from depending on the specifics of what we are trying to do? Or even as separate npm modules.

@taion
Copy link
Member

taion commented Jun 26, 2015

I think we want both.

By default, fetch should return PENDING and FAILED sentinel values that the container can handle using the current logic.

In the event that the user wants additional control, he can specify custom pending and failed values for the fetch, that will appear to the container as "done" values, and then handle these in the component's render function.

The former gives an easy way to get generic load handling logic. The latter allows extending to cover more complex cases without excessively cluttering up the container API. There's a little bit of redundancy, but I think the use cases differ enough that it'd be justifiable to have both.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants