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
Bad state: Tried to use StateNotifier after dispose
was called.
#496
Comments
It sounds like this is a bug in your logic instead It is likely that your StateNotifier was rebuilt during an Consider passing ProviderReference to your StateNotifier and use ref.read instead. |
The specifical problem comes from: final User fetchedUser = User.fromJson(rawUser);
User sessionUser;
if (fetchedUser.confirmed != false) {
tokenController.state = fetchedUser.token!;
final Profile sessionUserProfile =
await profileRepository.findByUserId(fetchedUser.id);
sessionUser = fetchedUser.copyWith(profile: sessionUserProfile);
} else {
sessionUser = fetchedUser;
} It still does not work. Why? |
The problem is not those lines. It is that those lines are placed after an Your StateNotifier was disposed & recreating during the |
Using final authRepositoryProvider = Provider<AuthRepository>(
(ref) => GraphQLAuthRepository(client: ref.read(graphQLClientProvider)),
name: 'Auth Repository Provider',
);
final authNotifierProvider = StateNotifierProvider<AuthNotifier, AuthState>(
(ref) => AuthNotifier(
authRepository: ref.read(authRepositoryProvider),
dataStore: ref.watch(dataStoreRepositoryProvider),
profileRepository: ref.watch(profileRepositoryProvider),
tokenController: ref.watch(tokenProvider.notifier),
userController: ref.watch(userEntityProvider.notifier),
),
name: "Authentication Notifier Provider",
); Now, that |
Remi is right, I had exactly the same problem today. A StateNotifierProvider watches an object, then it updates this object. The function cannot reach its end because the StateNotifierProvider is rebuilt because of the "watching" of the object and it triggered exactly the same error message that you got. This is a kind of "loop" where something watches an object but also updates this object so get rebuilt. It's a matter of use and not a bug 😉 I think the problem is here: userController.state = sessionUser; You update the state of userController whereas your StateNotifier is watching userController. |
Actually, the problem is here: @julienlebren how did you solve the problem them? |
Yep that's exactly the same kind of line, it updates a state which is watched by your StateNotifier, causing its whole rebuild. In fact, you don't need to final authNotifierProvider = StateNotifierProvider<AuthNotifier, AuthState>(
(ref) => AuthNotifier(
authRepository: ref.read(authRepositoryProvider),
dataStore: ref.watch(dataStoreRepositoryProvider),
profileRepository: ref.watch(profileRepositoryProvider),
tokenController: ref.watch(tokenProvider.notifier),
userController: ref.watch(userEntityProvider.notifier),
),
name: "Authentication Notifier Provider",
); You are just passing your There are many solutions to this, one of the solutions would be to pass final authNotifierProvider = StateNotifierProvider<AuthNotifier, AuthState>(
(ref) => AuthNotifier(ref.read),
name: "Authentication Notifier Provider",
); Your class AuthNotifier extends StateNotifier<AuthState> {
AuthNotifier(
this.read,
) : super(AuthState.initial()); // put your initial state here
final Reader read;
} Then in your login method: final User fetchedUser = User.fromJson(rawUser);
User sessionUser;
if (fetchedUser.confirmed != false) {
read(tokenController).state = fetchedUser.token!;
final Profile sessionUserProfile =
await read(profileRepository).findByUserId(fetchedUser.id);
sessionUser = fetchedUser.copyWith(profile: sessionUserProfile);
} else {
sessionUser = fetchedUser;
} Aditionnally, in your initial code, please note that you should not use |
What's about And, The workflow right now, as we understand is:
This is the workflow who gives the issue now. |
class AuthState {
const factory AuthState.loggedIn(User user) = _Authed;
} Then in your view: class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ProviderListener<AuthState>(
provider: authNotifierProvider,
onChange: (context, state) {
state.maybeWhen(
loggedIn: (user) {
context.read(tokenController).state = user.token!;
context.read(userController).state = user;
},
orElse: () => null,
}
},
child: ...,
);
}
}
The first solution I gave is the best imho, since you have a bunch of stuff to do inside your notifier, passing a |
Cool, We tried this one, and, it seems to be working corretly man, thank you! |
How to fix this when stateNotifier needs an async function? |
I also had a StateNotifier needing to be initialized with an async function, juste like this : What I did was to check that my StateNotifier was mounted before changing its state : Future<int> fetch() async =>
Future.delayed(const Duration(seconds: 1)).then((value) => 42);
class Whatever extends StateNotifier<AsyncValue<int>> {
Whatever(): super(const AsyncValue.loading()) {
_fetch();
}
Future<void> _fetch() async {
state = const AsyncValue.loading();
try {
final result = await fetch();
if (mounted) {
state = AsyncData(result);
}
} catch (e, s) {
state = AsyncError(e, s);
}
}
} |
Thank you @icodeyou , I have solved the issue I found in a similar way to yours. |
What I learned from my issue was : If you are using a stateNotifierProvider.autoDispose<>... then make sure that it is watched somewhere because the autoDispose ensures that the provider is created only when it is used somewhere in screen(is watched somewhere)....so use final temp = useProvider(providerName).... |
@icodeyou Thank you for the solution, but do you know any way we could stop completely the function that is running without have to manually check Or there is a way to stop running function right at the moment mounted turn false ? Cause after state = ..... There are maybe some unnecessary expensive operation that no longer need to be called. |
@dongnqda if dont want check multiple times you can override?
|
Hi!
StateNotifierProvider:
and StateNotifier
There is the only authProvider await before the initialization of this provider, but start after loading auth data. |
Describe the bug
We are using a StateNotifier to handle our application's workflow; in this case, we are handling the signin workflow, but at finsihing the process, or along itself, the
StateNotifier
is disposes before to finish is workflow:To Reproduce
This is the
StateNotifierProvider
:This is the function that loges in the user and gives the bug:
Specifically the last one.
It follows the workflow correctly:
Expected behavior
After finishing the process, it should finish the function and leave us to continue with the next screen.
The text was updated successfully, but these errors were encountered: