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

Dispatching an action that does not alter state rerenders the widget, causing an infinite loop #243

Closed
Lilja opened this issue Mar 3, 2023 · 5 comments

Comments

@Lilja
Copy link

Lilja commented Mar 3, 2023

Very new to flutter.

I'm currently experiencing something which is weird in my book. I have a store that I'm connecting. Because of the lack of onMounted or equivalent in react(coming from vue) I would like to trigger a side effect once when the widget renders. To do this I tried to dispatch something and then catch is as a middleware. But something is not working I feel like.

My widget is essentially this:

return StoreConnector<RedditState, RedditVM>(
    builder: (context, redditVM) {
        redditVM.pingSubreddit(subreddit);
        // [...] omitted rest of component.
    }
}

Looking at pingSubreddit, you get this: store.dispatch(5). The reducer is checking the action type and performing logic accordingly. There is nothing that is supposed to happen when an integer is passed.

For some reason, this is rendering an infinite loop. I suppose that this package will always take a reducer and render the new components below it. Is this intended? I tried to replicate this using React's redux package by doing a dispatch during render. But it doesn't look like that forces an infinite loop.

@Lilja
Copy link
Author

Lilja commented Mar 4, 2023

Example:

import 'package:flutter/material.dart';
import 'package:redux/redux.dart';

enum Actions { increment }

int counterReducer(int state, dynamic action) {
  return action == Actions.increment ? state + 1 : state;
}


void main() {
  runApp(StoreProvider<int>(
      child: MaterialApp(
          home: Scaffold(
              body: StoreConnector<int, VoidCallback>(
                  builder: (context, vm) {
                    print("render");
                    vm();
                    return const Text("data");
                  },
                  converter: (store) => () => store.dispatch("Foo")))),
      store: store));
}
Restarted application in 927ms.
2064 I/flutter (17550): render
Application finished.
Exited (sigterm)

@brianegan
Copy link
Owner

brianegan commented Mar 4, 2023

I believe you're looking for the onInit callback function which can be passed to the StoreConnector constructor.

https://pub.dev/documentation/flutter_redux/latest/flutter_redux/StoreConnector/onInit.html

If you dispatch an action inside the builder function, it will send that action to the Redux reducer, which triggers a state change, which triggers a rebuild, which runs the builder function, which dispatches again, which calls the recuer, produces a state changes, calls the builder... and you've found yourself in an infinite loop :)

Some sample code below (might be missing a paren somewhere -- github coding and didn't run it locally, but should get the idea across).

import 'package:flutter/material.dart';
import 'package:redux/redux.dart';

enum Actions { increment }

int counterReducer(int state, dynamic action) {
  return action == Actions.increment ? state + 1 : state;
}


void main() {
  runApp(StoreProvider<int>(
      store: store
      child: MaterialApp(
          home: Scaffold(
              body: StoreConnector<int, int>(
                  // Grab the current count from the store
                  converter: (store) => store.state,
                  // A function that runs once when the StoreConnector is inserted into the widget tree
                  onInit: (store) => store.dispatch(increment),
                  // Should be a pure function that returns a Widget tree and performs no side effects.
                  builder: (context, counter) {
                    return const Text("current count $counter");
                  },
        ),
      ),
  );
}

@Lilja
Copy link
Author

Lilja commented Mar 4, 2023

Thanks @brianegan totally forgot onInit. It solves what i'm trying to do.

However, is it intended that when new state and old state is being reduced will still produce a rerender? Isn't that a bit wasteful?

@brianegan
Copy link
Owner

brianegan commented Mar 4, 2023 via email

@Lilja
Copy link
Author

Lilja commented Mar 4, 2023

Thanks! I'll have a look and reach out if I need more help. Much appreciated.

@Lilja Lilja closed this as completed Mar 4, 2023
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