alpinejs / alpine

A rugged, minimal framework for composing JavaScript behavior in your markup.
https://alpinejs.dev
MIT License
28.32k stars 1.23k forks source link

TypeError: proxy set handler returned false for property #132

Closed TomS- closed 4 years ago

TomS- commented 4 years ago

I'm unsure if this will be fixed, or ever could be fixed without a huge refactor. But I thought I'd just check to see if it's anything I'm doing wrong. I want to store a slider in the proxy so I can access it for further functions. In this example I want to select the next slide when I fire a click @click="nextSlide()". To do this I've stored the slider as data. However, it's returning the error TypeError: proxy set handler returned false for property. The code is:

let flkty = new Flickity(el, {
                    wrapAround: true,
                    prevNextButtons: false,
                    pageDots: false,
                    setGallerySize: el == this.$refs.slider ? false : true,
                    selectedAttraction: 0.01,
                    friction: 0.15
});

this.slider = flkty;

Thanks

rzenkov commented 4 years ago

I don't understand where is issue. Your code lacks of clarity.
But here is example, all things works fine for me. Slider stored in scope, buttons uses slider. JsBin

TomS- commented 4 years ago

@rzenkov how bizarre, my code is pretty much the exact same as yours:

export function blockSlider() {
    return {
        x: 0,
        y: 0,
        sliders: [],
        init() {
            [this.$refs.slider, this.$refs.overlaySlider].forEach((el) => {
                let flkty = new Flickity(el, {
                    wrapAround: true,
                    prevNextButtons: false,
                    pageDots: false,
                    setGallerySize: el == this.$refs.slider ? false : true
                });

                this.sliders.push(flkty);
            });
        },
        changeSlide(dir) {
            this.sliders.forEach((el) => {
                if(dir == 'next') {
                    el.next(true);
                } else {
                    el.previous(true);
                }
            });
        }
    }
}

Might be something wrong with my build process?

rzenkov commented 4 years ago

Imho issue occurs not with sliders... Somewhere else..

TomS- commented 4 years ago

@rzenkov yup, you're right. Tried just doing a small component:

export function statCounter() {
    return {
        count: 0,
        init() {
            this.count = 1200;
        }
    }
}

HTML:

<div class="my-48">
    <div class="container px-4 mx-auto text-center font-display" x-data="statCounter()" x-init="init()">
        <?= $value->title ?>
        <span class="block text-6vw" x-text="count"></span>
    </div>
</div>

This is giving the error TypeError: proxy set handler returned false for property '"count"'

EDIT:

export function statCounter() {
    return {
        a: 0,
        init() {
            return () => {
                this.a = 1200;
            }
        }
    }
}

This worked. Though I want to change the value when Alpine is initiated but the DOM hasn't been minipulated.

rzenkov commented 4 years ago

try to use this.$nextTick(()=>{/* your code */}).

From docs: $nextTick is a magic property that allows you to only execute a given expression AFTER Alpine has made it's reactive DOM updates

TomS- commented 4 years ago

Strange however, it works in your example without the need to do that. I can not fathom any difference between your jBin and my code. It has to be my build process that is throwing it out of wack some how. In the meantime until I figure out why, I'll use $nextTick.

rzenkov commented 4 years ago

issue appears when you doing something before Alpine was ready. You simply need to split up code, main part runs in function itself, sensitive part in $nextTick callback

x-init allows do the same thing by returning function expression, then you can split you code in init function one part in init itself, other - in callback.

function scope(){
    return {
      init(){
          // some initialization
          return () {
             // sensitive part
          }
    }
}

that is why your second example works

SimoTod commented 4 years ago

The error in console is because your build process force javascript to run in strict mode.

This is due to line https://github.com/alpinejs/alpine/blob/5fb81f5434ffc97e0a9a76513415f0f3dba5dced/src/component.js#L79 where we should return the result of the set operation, not a falsy value when the reactivity hasn't been enabled yet (in your case because you are setting a property on init, the second example works because it returns a function so, as per documentation, the execution is deferred and the reactivity is enabled again at that point).

There's nothing wrong in your code.

This is a pen to reproduce the error with the standard build: https://codepen.io/SimoTod/pen/ZEYPrdQ