django-unicorn
django-unicorn copied to clipboard
updated() broken?
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:
- The
updated()
method is never called - 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?
What I forgot: the context (and hence {{ counter }}
is updated correctly in the visible HTML. Just the value in updated*
is lagging behind.
I have same problem
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.
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.
I was changing the value from the front-end - just by clicking the "inc" button...
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?
sure, one can call it manually if needed.
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 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!