Skip to content

useEffect() with non primitive values used in dependencies causes unwanted side effects.  #469

@jaybe78

Description

@jaybe78

Describe the bug

useEffect() with non primitive values used in dependencies causes unwanted side effects.
For example a list in a parent component is passed to a child component, which is used as a dependency of a useEffect.

When that child component updates the state, the useEffect callback gets called again, while the list has not changed one bit.

The only way to prevent what seems to me an incorrect behaviour is to do [list.toString()]

I guess useEffect works on reference to compare data, and therefore when converting array to string, it's working as expected because it's a primitive value.

To Reproduce

Let's say I have a parent component with an array of string like

List<String> list = ["a", "b", "c"]

Then I have a child component

class SimpleComponent extends HookWidget {
 final List<String> list;

const SimpleComponent({required this.list, super.key});

  @override
  Widget build(BuildContext context) {
    final my_list = useState<List<String>>(list);

    useEffect(() {
      list.value = list;
      return () {
        print("unmount");
      };
    }, [list]);

    return CustomTextButton(
        onPressed: () {
          list.value = [...list.value, "hello"]
        },
        textColor: Colors.red,
        text: "Add more text to the list");
  }
}

The example above will trigger side effects every single time you modify the list in the child component, while the list coming from the parent has not changed at all.

This is a big problem, when you have complex component where a variable can be updated from the parent and the children.
this behaviour can totally break the consistency of the component.

the only way to fix it is:

    useEffect(() {
      list.value = list;
      return () {
        print("unmount");
      };
    }, [list.toString()]);

Expected behavior
If an array, or object is used as a dependency in useEffect, there should not be any side effect as long as the dependency has not changed.

I come from react world and it's an issue I already experienced and I think back then I had used another library (use-deep-compare), which allowed to actually compare object and array rather than just primitive values

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions