django-unicorn icon indicating copy to clipboard operation
django-unicorn copied to clipboard

updated() broken?

Open nerdoc opened this issue 2 years ago • 9 comments

I experience problems with the component's update() method. I created a simple test component, with one counter property and an inc() method. And to update* methods:

class FooView(UnicornView):
    counter: int = 1

    def inc(self):
        self.counter += 1

    def updated_counter(self, value):
        print(f"updated_state: {value}")

    def updated(self, name, value):
        print(f"updated: {name}, {value}")

The html is just the {{ counter }} and a button that calls inc().

Now, after first launch (counter=1) I would suspect that after clicking on the button, counter becomes 2, and the server should output 2 lines:

updated: counter, 2
updated_counter: 2

But instead, it outputs:

updated_counter: 1

Problems:

  1. The updated() method is never called
  2. The updated_counter() method always outputs the last (!) value - the one before the change happens.

If i add another button with set unicorn:click="counter=4" in the button, and click oce the first (inc) button, and then that one, the output is:

updated_counter: 2
updated_counter: 4
updated: counter, 4

Ok, so after setting the property per frontend, at least the updated() method is called. But I would expect that when I do a self.counter += 1 too. And I don't know why the updated_counter: 2 even is printed there. That's too late, it looks like it would have been waiting in a queue somewhere?

I tried unicorn 0.44.1, 0.42.0, 0.40.0, 0.37.0 just to get a few versions Any clues? Am I doing something terribly wrong, or is this a (really severe?) bug?

nerdoc avatar Apr 04 '22 13:04 nerdoc

What I forgot: the context (and hence {{ counter }} is updated correctly in the visible HTML. Just the value in updated*is lagging behind.

nerdoc avatar Apr 04 '22 13:04 nerdoc

I have same problem

roman-tiukh avatar May 09 '22 12:05 roman-tiukh

Thanks for reporting, let me take a look. I have a theory, but I want to investigate some more to see what is going on.

adamghill avatar May 14 '22 13:05 adamghill

Ok, so what is happening is this: updated_* and updated were originally only designed to fire when setting a component's property from the front-end. It was mostly a way to tie a change in the template to something in the component. If you are already in the component writing code, I'm trying to to figure out how useful this would be. However, can you tell me more about what you are trying to do to make sure I understand the use-case?

One worry I have is that it could create an infinite loop in some situations. For example, if updated_counter had self.counter += 1, but I think I could figure out an ok approach for that.

adamghill avatar May 14 '22 20:05 adamghill

I was changing the value from the front-end - just by clicking the "inc" button...

nerdoc avatar May 14 '22 22:05 nerdoc

I understand that. What I meant was setting a component property from the frontend, i.e. <button u:click='counter=2'>set value</button> as opposed to calling a component method. updated_ was designed to give a hook in the component code when a value changes in the frontend. Why would you need updated_* called after a component method is updated in the component?

adamghill avatar May 15 '22 15:05 adamghill

sure, one can call it manually if needed.

nerdoc avatar May 15 '22 20:05 nerdoc

I would like to know when the value of a particular state has changed through updated_*, but I have this method called before and after hydrate(). Calling before hydrate() gives the old values and is called every time the component hydrates, whether or not the value has changed, and calling after hydrate() gives the already changed value.

I would suggest having something like changed_* so that you can react to changes in the state of the component. And I would also suggest that the component have a private property _is_hydrated so that you can understand if the updated_* method was called before or after hydrate().

roman-tiukh avatar May 15 '22 20:05 roman-tiukh

@roman-tiukh That's interesting! Any chance you can fork https://github.com/adamghill/django-unicorn/pull/407 and provide some code that I can use to replicate? Ideally with some comments for what you would expect to happen as well. Or add some failing tests to show your expectations. Thanks!

adamghill avatar May 16 '22 02:05 adamghill