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

Is there a way to refresh data? #6

Closed
nsxz1234 opened this issue Nov 23, 2023 · 7 comments
Closed

Is there a way to refresh data? #6

nsxz1234 opened this issue Nov 23, 2023 · 7 comments

Comments

@nsxz1234
Copy link

swappy-20231123_195720

when I use riverpod, I can use ref.invalidate()

swappy-20231123_200122

@jinyus
Copy link
Contributor

jinyus commented Nov 23, 2023

ref.invalidate just re executes the future that's used to compute the initial state. I don't think it's possible with the current design, because signalFromFuture returns a ReadOnlySignal which can't be modified from the outside, so it would need to expose a function that can re-execute the future that was passed in.

I modified the code to get this working but it's very hacky and changes the public API so lets wait to hear what the maintainer has to say about this.

@rodydavis
Copy link
Owner

I am totally open for it. I also used this a lot in Riverpod.

I think how the fix for the widget effect subscribing was fixed with a set, we could do the same here.

But I like returning a read only by default and introducing a mutable signal as another callback.

It is open for discussion though!

@rodydavis
Copy link
Owner

@jinyus can you share the hack?

@jinyus
Copy link
Contributor

jinyus commented Nov 24, 2023

@rodydavis Since the signal cannot be updated from the outside, I returned a record with the signal along with a function to invalidate the state. I also removed an unnecessary subscription and the start variable.

https://github.com/rodydavis/preact_signals.dart/blob/main/packages/preact_signals/lib/src/extensions/future.dart#L16

({ReadonlySignal<SignalState> signal, VoidCallback refresh})
    signalFromFuture<T>(Future<T> Function() getFuture) {
  final s = signal<SignalState>(SignalLoading());

  Future<SignalState> execute() async {
    try {
      return SignalValue(await getFuture());
    } catch (e) {
      return SignalError(e);
    }
  }

  init() {
    if (s.peek() is! SignalLoading) {
      s.value = SignalLoading();
    }
    execute().then((value) => s.value = value);
  }

  init();

  return (signal: s, refresh: init);
}

Another limitation I discovered is that once a future completes, it cannot be reset so no loading state would appear when refreshing, it would just return the old value instantly...so signalFromFuture now takes a function that should return a unique future, downside is that the onus is on the caller to make sure the future is unique. ie: not const:

Good: Future.delayed(Duration(seconds: 1), () => 'abc')
Bad: Future.delayed(const Duration(seconds: 1), () => 'abc')

edit: It looks like this isn't a problem with real world scenarios. as Future.delayed(const Duration(seconds: 1), () => getStr()) works fine.

In flutter:

final futureSignal = signalFromFuture<String>(
  () => Future.delayed(Duration(seconds: 1), () => 'abc'),
);

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  void _reloadData() {
    futureSignal.refresh();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Builder(builder: (context) {
              final state = futureSignal.signal.watch(context);
              final text = switch (state) {
                SignalError(value: var v) => 'Error: $v',
                SignalValue(value: var v) => 'Success: $v',
                _ => 'Loading...',
              };
              return Text(
                text,
                style: Theme.of(context).textTheme.headlineMedium!,
              );
            }),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _reloadData,
        child: const Icon(Icons.refresh),
      ),
    );
  }
}
flutter_signal_demo.mp4

@jinyus
Copy link
Contributor

jinyus commented Nov 24, 2023

@rodydavis I solved the problem in a cleaner way by creating a FutureSignal class that extends Signal, just created a PR.

@rodydavis
Copy link
Owner

Can we do the same for Streams?

@rodydavis
Copy link
Owner

Fixed with: #7

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