skip to Main Content

This question is a duplicate of Flutter Redux and Hook. How to watch variable instance changing inside useEffect like react? but with a simplified and generic use case.


I’m using flutter_hooks and flutter_redux

Here is my widget:

class Root extends HookWidget {
  const Root({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    final store = StoreProvider.of<AppState>(context);

    store.onChange.listen((AppState appState) {
      print('store.onChange print: ${store.state.init}');
    });

    useEffect(() {
      print('useEffect print: ${store.state.init}');
      store.dispatch({'init': true});
    }, [store.state.init]);

    return Container();
  }
}

Current ouput:

useEffect print: false
store.onChange print: true

Expected ouput:

useEffect print: false
store.onChange print: true
useEffect print: true

Why does useEffect hook is not trigger when I update the state of the store while store.onChange is clearly trigger ?

Edit

In react i can do something like this:

import React from "react";
import { useAppDispatch, useAppSelector } from "../../app/hooks";
import { RootState } from "../../app/store";
import Box from "@mui/material/Box";
import { set } from "../../app/globalSlice";

const MyComponent = React.memo(() => {
  const dispatch = useAppDispatch();
  const init = useAppSelector((state: RootState) => state.globalState.init);

  React.useEffect(() => {
    console.log("init state: " + init);
    if (init === false) {
      dispatch(set({ init: true }));
    }
  }, [init]);

  return <Box />;
});

export default MyComponent;

output:

init state: false
init state: true

Notive that the useEffect function has been call twice without having to use setState.

Is there a way to do the same in flutter ?

2

Answers


  1. Chosen as BEST ANSWER

    I found the answer here: https://github.com/brianegan/flutter_redux/issues/244

    This library does not support flutter hooks out of the box, since it is a 3rd party package in the Flutter ecosystem. This library is only built to work with flutter + redux.

    Please see alternative packages, such as https://pub.dev/packages/redux_hooks to get that functionality!


  2. You are changing the state of your store, but you are not calling setState() on the widget. And if you are using redux you should use StoreConnector widget inside your build method so as to properly update the state when store changes.

    Your store’s print log is called because listen on a stream subscription will always be called when you are updating your store. But useEffect will only be executed when Widget is being rebuilt.

    Currently you are dispatching a state update in useEffect() hook but nowhere setState() is called to update the current widget. And because of this build method is not called again so useEffect hook is not called again.

    The useEffect function is called every time a widget is being rebuilt but the widget is only being rebuilt once because changes in store doesn’t apply those changes by calling setState() on the widget.

    So your widget is being built only one time so your logs show only one useEffect print statement but many store change statements.

    To solve the issue, you should do the following:

    //Inside the build method
    useEffect(() {
      //Your logic here.
      setState((){});
    });
    

    Also, you should move the store.state.onChange.listen to the initState of the widget so as to prevent memory leaks because widget’s build method is called many times and you don’t want to create a new stream subscription every time you call the build method, so that you can properly dispose off the stream subscription after widget is unmounted in dispose.

    flutter_redux provides a StoreConnector widget which does what you want. It automatically rebuilds the widget without calling setState((){}) explicitly. So you could do something like this:

    So where you use your Root widget your could wrap that widget with store connector.
    For ex:

    MaterialApp(
      child: Scaffold(body: StoreConnector<AppMainState, bool value>(
                        builder: (context, value) {
    //You can either pass the init value to the widget or access it directly in Root widget as you were doing. When the init value changes it will automatically rebuild the Root widget.
                          return Root(init: value);
                        },
                        converter: (store) => store.state.init),),
    );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search