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

Store not updating state #111

Closed
ArthurianX opened this issue Mar 4, 2019 · 9 comments
Closed

Store not updating state #111

ArthurianX opened this issue Mar 4, 2019 · 9 comments

Comments

@ArthurianX
Copy link

Hey @brianegan !

This is a cool library, I have a problem and I'm not sure it's a flutter_redux issue or human error.

I have a store, in which I keep a pageOffset: 0 value, I'm using that to fetch paginated data from an api.

The flow is like this:

  • I instantiate the store
  • I'm launching the first fetch action
  • loading / success actions and their respective middleware is triggered / populated.
    At this point, I scroll at the bottom of my list, and a new PAGED action is triggered to get me the next page of items
  • I'm triggering an action to increase pageOffset by 1
  • I'm getting the data (actions load / success)
  • I'm checking the pageOffset and it is 1, as it should be
  • In a middleware I merge existing content with new content, all ok
  • returning said content in a new action, it appears in my widget
    Now, I'm scrolling again to the bottom and fetch the next page, but pageOffset is 0.

I'm not sure what I'm missing here's some snippets of code:

Store / State

import 'package:g4mediamobile/src/services/g4media_search_api.dart';
import 'package:g4mediamobile/src/services/githup_search_api.dart';

enum CurrentScreen { home, about_us, settings, contact, feedback }

class G4Store {
  // final SearchResult result;
  final FetchPostsResult posts;
  final SearchResult result;
  final bool hasError;
  final bool isLoading;
  final bool darkTheme;
  final int pageSize;
  final int pageOffset;
  final int currentPost;
  final CurrentScreen currentScreen;

  G4Store({
    this.posts,
    this.result,
    this.hasError = false,
    this.isLoading = false,
    this.darkTheme = false,
    this.pageSize = 15,
    this.pageOffset = 0,
    this.currentPost,
    this.currentScreen = CurrentScreen.home,
  });

  factory G4Store.initial() =>
      new G4Store(result: SearchResult.noTerm());

  factory G4Store.loading() => G4Store(isLoading: true);

  factory G4Store.error() => G4Store(hasError: true);

  factory G4Store.pageOffset(offset) => G4Store(pageOffset: offset + 1);
}

Part of the PAGED middleware ]

Stream<dynamic> _search(FetchPostsEnumType fetchType, store) async* {
    // Dispatch a SearchLoadingAction to show a loading spinner
    yield FetchPostsLoadingAction();

    var query = '';
    if (fetchType == FetchPostsEnumType.paged) {
      query = '?per_page=${store.state.pageSize}&offset=${store.state.pageOffset + 1}';
      yield FetchPostsChangeOffsetAction(store.state.pageOffset);
    } else {
      // Yield to reset page offset ?! pfffbbttt
    }

    try {
      // If the api call is successful, dispatch the results for display
      yield FetchPostsProcessAction(await api.search(query));
    } catch (e) {
      // If the search call fails, dispatch an error so we can show it
      yield FetchPostsErrorAction();
    }
  }

Reducer:

import 'package:redux/redux.dart';
import 'package:g4mediamobile/src/state/actions.dart';
import 'package:g4mediamobile/src/state/g4_store.dart';

final searchReducer = combineReducers<G4Store>([
...

  // Increase Page Offset
  TypedReducer<G4Store, FetchPostsChangeOffsetAction>(_onChangeOffset),
]);

.
.
.
G4Store _onChangeOffset(G4Store state, FetchPostsChangeOffsetAction action) =>
    G4Store.pageOffset(action.pageOffset);

My opinion is that the state is not updated by the time I scroll down again (it's not a time/repaint thing, I can wait however long)

Also, this is my first week with Flutter, I might be misunderstanding something really basic.

Thanks!

@ArthurianX ArthurianX changed the title Store not changing state on state read Store not changing state on reading state again. Mar 4, 2019
@ArthurianX ArthurianX changed the title Store not changing state on reading state again. Store not updating state Mar 4, 2019
@ArthurianX
Copy link
Author

image

You can see pageOffset being updated here, on the next call is 0, I've checked to see if I'm reinitializing the store or something, but it's not the case.

@brianegan
Copy link
Owner

brianegan commented Mar 4, 2019

Hey hey :)

Glad it's useful, and welcome to the land of Flutter! Hope you're enjoying it!

Now, this is a bit tough to debug without the full source, but is there any way you're recreating the State class using one of the initial, loading, or error constructors? Those appear to reset the pageOffset to 0.

If it were me, that's probably the first place I'd be looking. Furthermore, it might be a good idea to add the redux_logging package to your app and supply a custom formatter that prints the Action and the current pageOffset. I imagine you'll see an action that is dispatched and resets it back to 0.

Let me know if that helps!

@ArthurianX
Copy link
Author

Man that was quick, initial is being called only in the main widget, but loading is being called on each api action,

factory G4Store.loading() => G4Store(isLoading: true); resets the whole store ?

I don't know that much dart yet but I imagined it's only changing that property.

You know what, it's a public repo, I'm making a push now with my changes.

It's here: https://github.com/ArthurianX/g4media-mobile/commits/master

@ArthurianX
Copy link
Author

So, it's a mess in that repo, I've started my work based on one of your demos, and I'm gradually understanding things.

@brianegan
Copy link
Owner

brianegan commented Mar 4, 2019

Nope, that means that it's constructing an entirely new instance of the State each time and wiping out any previous state. In general, I use a copyWith method to make life easier, something like this:

https://github.com/brianegan/flutter_architecture_samples/blob/master/redux/lib/models/app_state.dart#L23

And I use it like so:

final myState = MyState(loading: false, pageOffset: 0);

// Start Loading
myState.copyWith(loading: true); // Create a new instance but only updates loading status

// Page Update when data comes back
myState.copyWith(loading: false, pageOffset: myState.pageOffset + 1);

@ArthurianX
Copy link
Author

image

Really cool! Thanks!

I'll have to do some refactoring!

Looking at your examples, from what I understand you are copying and passing the WHOLE state around on each dispatch and all the places you call it from, is this ok ? I'm asking this because in my case I'm planning to save ~100 articles in my state, and copying it around like that on each action call seems ... heavy.

Am I wrong?

@brianegan
Copy link
Owner

from what I understand you are copying and passing the WHOLE state around on each dispatch and all the places you call it from, is this ok

Yep, this shouldn't be a big deal. In the Todos example, if the action does not affect a part of the substate it will return the OLD value. Therefore, it's not re-creating that data, it's just passing the old reference to the new State instance! Therefore, the State will be new, but the references (the heavy bit) will all be reused.

Glad it was helpful :)

@ArthurianX
Copy link
Author

Thanks a lot @brianegan !

Have a great day!

@brianegan
Copy link
Owner

Sure thing, you too!

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

2 participants