Giter Site home page Giter Site logo

Force fetch remotely about marty HOT 13 CLOSED

martyjs avatar martyjs commented on July 28, 2024
Force fetch remotely

from marty.

Comments (13)

jhollingworth avatar jhollingworth commented on July 28, 2024

Hey, good question. remotely will be called if locally returns undefined (or it doesn't return anything). So if your store keeps track of the current page you could do something like this:

@.fetch
  id: 'all-people'
  locally: () ->
    return @state.people if @state.meta.currentPage == pageNumber
  remotely: () ->
    PeopleAPI.findPeople(params)

I'm wondering, would it be worth caching pages of users locally? Something like this

@PeopleStore = Marty.createStore
  getInitialState: ->
    people: []
    meta:
      totalPages: 0
      pagesLoaded: {}

  findPeople: (params) ->
    pageNumber = if params and params.pageNumber then params.pageNumber else 1

    @.fetch
      id: 'page-#{pageNumber}'
      locally: () ->
        if @state.meta.pagesLoaded[pageNumber]
          return _.where(@state.people, pageNumber: pageNumber)
      remotely: () ->
        PeopleAPI.findPeople(params)

  receivePeople: (response) ->
    @state.totalPages = response.meta.totalPages
    @state.pagesLoaded[response.meta.page] = true
    @state.people = @state.people.concat(response.people)
    @.hasChanged()

from marty.

bigardone avatar bigardone commented on July 28, 2024

Thanks @jhollingworth for your response, it totally make sense, now I've got it working! :)

from marty.

nhagen avatar nhagen commented on July 28, 2024

So in this example, page change invalidated the catch which is convenient.

What if you have a master list that you always want to reflect the latest snapshot of the database? For instance, if I have a view where i always want to invalidate the local? Nothing is changing with the meta state of the app, only the data is being updated.

I'm running into an infinite loop issue because I'm using the state mixin, which executes the getAll store method every time theres a change to the store (which is every time that I call getAll). Is the only solution to just not use the state mixin, and to not subscribe to changes? How could I use the fetch state (pending, etc) in the view in that case?

var OrderStateMixin = Marty.createStateMixin({
  listenTo: OrderStore,
  getState: function() { return { orders: OrderStore.getAllOrders({ cache: false });
});

var OrderStore = Marty.createStore({
  getAllOrders: function(options) {
    options = options || { cache: true };
    return this.fetch({
      id: 'GET_ALL_ORDERS',
      locally: function() {
         return !options.cache ? this.state.orders : undefined;
      },
      remotely: function() {
         return OrderApi.getAllOrders();
      }
    });
  },

  receiveOrders: function(orders) {
    this.setState({ orders: orders });
  }
});

from marty.

bigardone avatar bigardone commented on July 28, 2024

Hi @nhagen
Maybe you can use a state attribute of the store to control that, something like:

    var OrderStore = Marty.createStore({
      getAllOrders: function(options) {
        options = options || { cache: false };

        this.state.cache = options.cache;

        return this.fetch({
          id: 'GET_ALL_ORDERS',
          locally: function() {
             return this.state.cache ? this.state.orders : undefined;
          },
          remotely: function() {
             return OrderApi.getAllOrders();
          }
        });
      },
      receiveOrders: function(orders) {
        this.setState({ orders: orders, cache: true });
      }
    });

So every time it fetches for the first time locally it returns undefined forcing fetching remotely, which sets then sets cache to true so the second local fetch will return the state projects. The next call to projects from the mixin will pass cache equal false starting all over again.

Hope this helps :)

from marty.

rottmann avatar rottmann commented on July 28, 2024

@bigardone the problem from @nhagen (and currently for me too) is e.g. a rendering with pending and done output.

When you fetch remotely, render will be called with promise status pending, but before every render the getState will be called again with cache: false, which ends in an infinite loop.

The only hacky solution is a cache timeout after some seconds. Or am I missing something?

from marty.

jhollingworth avatar jhollingworth commented on July 28, 2024

@nhagen

The fetch API is designed for the case when you expect some state to be there but its not there right now. If I understand you correctly, you have some state in the store but its stale. If thats the case, I think you should have an action which is responsible for refreshing the state.

e.g.

var OrderActionCreators = Marty.createActionCreators({
  refreshOrders: Constants.REFRESH_ORDERS(function () {
    OrderAPI.updateAllOrders();
  });
})

setInterval(OrderActionCreators.refreshOrders, 100);

from marty.

nhagen avatar nhagen commented on July 28, 2024

Yup, that worked great. I call OrderActionCreators.refreshOrders on getInitialState in my store, and in OrderActionCreators.refreshOrders I have

var timeout;
var OrderAction ...
  refreshOrders: ...
    clearTimeout(timeout); 
    timeout = setTimeout(this.refreshOrders, 10000)

which sort of resets the timeout loop every time the user chooses to manually refresh. An interval of course would work just as well.

Having the refresh as an action ads a lot of flexibility. You can have user-level auto-refresh based on their user settings, or you can have a manual refresh button, or you can do a combination of the two. I think this is better UX and is less bug prone than having refreshes happen behind the scenes and outside of the normal Flux flow.

from marty.

grmlin avatar grmlin commented on July 28, 2024

I have to revive this issue, because I'm not sure if I use fetch correctly at the moment.

I love how marty containers handle all the fetch/loading code for me, so I want to use it. Anyway, when I'm navigating my app without page reloads I have to fetch the data remotely when a page opens. With fetch this is not possible, because it will be cached once and forever.

Right now I have a parent component in which I invalidate the cache so it's fetched remotely, again. My problem is, I have no idea how to do this in the container using the store itself.

What I want to do is this:

Marty.createContainer(ProfileShow, {
    componentWillMount(){
        UserStore.invalidateCache();
    },
    listenTo: UserStore,
    fetch   : {
        user() {
            return UserStore.for(this).getUser();
        }
    },
    pending() {
        return <div className='pending'>Loading user data...</div>;
    },
    failed(errors) {
        return <div className='error'>Failed to load user. {errors}</div>;
    }
});

The code above does not work as intended, because the componentWillMount method is called after the first fetch is made. Is there any other hook I can use to invalidate the cache before the fetch is done initially? Setting timeouts to invalidate the cache does not feel good, too.

It's a bit cumbersome to wrap everything with components so that I can force a remote fetch.

Thanks a lot for your help!

from marty.

taion avatar taion commented on July 28, 2024

BTW, in general you should not be directly mutating stores except via actions.

Why not just invalidate the cache when your component unmounts? Alternatively, keep track of whether or not this is the first fetch you've done, and invalidate the cache if so.

from marty.

taion avatar taion commented on July 28, 2024

An additional thing is that I've been using a strategy like the one proposed here: #80 (comment) where I force a remote fetch on component load, without explicitly invalidating what's currently in the cache.

from marty.

grmlin avatar grmlin commented on July 28, 2024

You are right. I directly mutated the store to simplify the example.

Anyway, on unmount will work in my case. Thanks! It will not work if the component remains and another one using this store is mounted. But this would be acceptable I think, if it ever happens...

I will look into your comment, too. I always ended in some sort of endless loop because the fetch methods gets called multiple times.

from marty.

taion avatar taion commented on July 28, 2024

I don't think the on-mount behavior you have is particularly well defined when you have multiple components accessing the same data either.

To get refresh working without infinite loops, you have to write it like:

  getData(id) {
    return this.fetchData(id, function () {
      return this.state[id];
    });
  }

  refreshData(id) {
    let refresh = true;

    return this.fetchData(id, function () {
      if (refresh) {
        refresh = false;
        return undefined;
      } else {
        return this.state[id];
      }
    });
  }

  fetchData(id, locally) {
    return this.fetch({
      id,
      locally,
      remotely() {
        return DataQueries.getData(id);
      }
    })
  }

from marty.

grmlin avatar grmlin commented on July 28, 2024

Well, I think when I use multiple components on a single page requesting the same data over and over again, my app structure is questionable anyway :)

Well, I did it sort of the way you describe above in the first place, but without fetch, locally and remotely because it added so much additional code ;)

I think I know what to do now, and how to add a refresh functionality. Thanks for your help!

from marty.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.