vue-masonry-css icon indicating copy to clipboard operation
vue-masonry-css copied to clipboard

vue-masonry-css makes first components loaded unreactive

Open ghost opened this issue 6 years ago • 24 comments

I'm facing a weird issue, the first components in the grid arent reactive anymore: see live

here is a previous production deployment , where everything works fine

🤔

ghost avatar Mar 20 '18 11:03 ghost

Could there be a conflict with the lazy-load/fade-in image plugin your using? Maybe theres a way to tell that plugin of the updated positions. Is it just the images or are you seeing other events breaking?

Nice showcase by the way!

paulcollett avatar Mar 20 '18 18:03 paulcollett

@paulcollett: thanks for the quick response

I'm glad you like it. I'm launching it tomorrow on Product Hunt :) I had to remove the grid feature in production unfortunately :(

Every events (@click...) and component methods (mounted()) are disabled for the first components. Here is the component where everything happen. Let me know if you find anything suspicious, or if you need more code to investigate.

Page/index.vue

ghost avatar Mar 20 '18 18:03 ghost

I'm getting the exact same thing!

mbarwick83 avatar Apr 04 '18 02:04 mbarwick83

@tchret what did you end up using?

mbarwick83 avatar Apr 04 '18 02:04 mbarwick83

@mbarwick83 did my own grid 🤡

ghost avatar Apr 10 '18 13:04 ghost

This seems reoccuring and something I'll look into.

@tchret Any tips on what you came up?

paulcollett avatar Apr 15 '18 20:04 paulcollett

Same issue here. When using other vue components inside masonry they become unresponsive.

justinkames avatar Jun 27 '18 09:06 justinkames

Hi there, just wanted to say the component is awesome and it's being incredibly useful and efficient with me. Unfortunately I also have this issue, trying to evoke a custom component @input event inside masonry. I'm also not sure of the best solution for so... Would there be any way to map any sort of event from masonry to it's child rendered components? Maybe using slots instead of creating the elements directly on template? I'm not sure. In any case, thanks for the simple solution!

mateuswetah avatar Aug 06 '18 17:08 mateuswetah

I'm quite late to the party and initial link is down already, but while using infinite-loading + progressive-img everything works like a char. Maybe something to consider?

helariL avatar Sep 06 '18 07:09 helariL

Hi,

I had such similar issue. I have an infinite wall, I add items in masonry depending on the scroll => no problem Sometimes I want to change all my data inside masonry => the data i changed, but the HTML of previously rendered components stays here

My workaround is to destroy masonry component before recreating it:

<masonry v-if="!killMasonry" :cols="5" :gutter="10">
        <div v-for="item of itemList.slice(0, maxNumber)">
          ...
        </div>
      </masonry>
...
data: () => ({
      maxNumber: 15,
      killMasonry: false,
    }),
    props: {
      itemList: Array
    },
    watch: {
      'itemList': {
        handler() {
          this.killMasonry = true;
          setTimeout(() => {
            this.killMasonry = false;
          }, 10);
        }
      }
    },
...

In my opinion, that's an heavy bug :(

ghost avatar Sep 19 '18 09:09 ghost

Have the same issue. Data changes in store do nor render to the screen. Kind of a deal braker for us ... Other than this, the plugin is very cool ... vote +1 for a fix ...

wouterfovel avatar Sep 21 '18 08:09 wouterfovel

@wouterfovel and @thibaultbrocheton just use :key inside transition to force rerender of the component

<transition>  
    <masonry 
        :key="columnsCount" 
        :cols="{ default: columnsCount, 1000: 2, 500: 1}" 
    ></masonry>  
</transition>  

Referenced from -> https://vuejs.org/v2/api/#key

helariL avatar Sep 21 '18 11:09 helariL

@helariL

<masonry :cols="{default: 3, 1000: 2, 700: 1}">	
    <v-flex v-for="dimension in dimensions" :key="dimension.id">
    ... --- Code allowing a dimension.name to change ---

After we change a dimension's name, the db is updated and the vuex state mutates to these changes (state.dimensions mutates). The adapted (state.)dimension.name is not altered on screen when <masonry ...> is used. If we remove <masonry ...>, it works fine ... We are not adding new columns in our case ...

wouterfovel avatar Sep 21 '18 11:09 wouterfovel

Same error here :( Actually all my components inside masonry are unresponsive. They trigger some stuff most of the time but the data doesn't update somehow... really weird

mydnic avatar Oct 27 '18 00:10 mydnic

Same issue, nothing in this thread has worked :(. I have to run a refresh after I load all the items and it's a deal breaker.

BoKKeR avatar Nov 04 '18 09:11 BoKKeR

We ran into the same issue and ended up using a Flexbox alternative, but first narrowed it down to a couple of problems:

  1. Any time the number of columns changes, contained elements lose reactivity
  2. When the component mounts, the reCalculate function is called twice: once with the default number of columns (2) and once with the number of columns you specified via props (in our case, 3). Since the number of columns has changed (2 -> 3), all contained elements lose reactivity.

Here's a simple Codepen demo illustrating the issues Elements in the 3-column layout are never reactive. Elements in the Responsive layout are only reactive in their default 2-column layout (try resizing the window).

The 2nd issue is also related to this check which returns early when previousWindowWidth is the same as current window width. Since reCalculate is called twice with different column values, _reCalculateColumnCount never gets called with the correct number of columns (3).
First recalculate:

  • displayColumns is 2
  • previousWindowWidth is undefined
  • _reCalculateColumnCount is called

Second recalculate:

  • displayColumns is 3
  • previousWindowWidth is the same as current window width
  • _reCalculateColumnCount is skipped

We were able to get around this by explicitly calling _reCalculateColumnCount and setting previousWindowWidth in the created hook instead of/in addition to mounted. This lets elements keep their reactivity as long as the number of columns is the same as when the component mounted.

As for the 1st issue, we think it has something to do with how Vue tracks key (maybe it's losing internal references to elements) but we switched gears before debugging any further. Hope that helps.

sbine avatar Nov 07 '18 23:11 sbine

Wrap all child components in a div. ie:

<masonry>
    <div>
        <some-component>
    </div>
</masonry>

mbarwick83 avatar Nov 08 '18 00:11 mbarwick83

Wrap all child components in a div. ie:

<masonry>
    <div>
        <some-component>
    </div>
</masonry>

That doesn't help. In @sbine codepen the components are already wrapped in div

mydnic avatar Nov 08 '18 09:11 mydnic

Am also facing this problem, which is a deal-breaker :( Inspecting with Vue DevTools shows an inconsistency with the DOM once the column counts are changed. For instance, something simple like a loading data property while false in Vue DevTools is effectively true on screen.

roberttolton avatar Nov 28 '18 03:11 roberttolton

I also am having this issue which is a real bummer because I really like this solution otherwise.

tcober avatar Dec 11 '18 23:12 tcober

@paulcollett you think you can find a solution?

mydnic avatar Dec 12 '18 08:12 mydnic

Hello everyone. So I think I found a solution to this problem for myself anyways. I had a pretty typical use case that is something like this:

<masonry :cols="{default: 2, 700: 1}" :gutter="30">
    <span v-for="number in [1,2,3,4,5,6,7]" :key="number">
        <basic-card />
    </span>
</masonry>

in my card component on re-size to 700px I would lose all my reactivity like everyone here. I read through @paulcollett 's script and saw that he was only grabbing the initial slot element for rerendering and had a sneaking suspicion that his script or Vue's render() function or something only cared about the initial child elements functionality, in this case that span tag. So I did an experiment like this:

<masonry :cols="{default: 2, 700: 1}" :gutter="30">
    <basic-card/>
    <basic-card/>
    <basic-card/>
    <basic-card/>
</masonry>

And sure enough on re-size my components kept their reactivity. SO, having done some work with Angular I wondered if there was some way to create that loop without nesting in another element like with ng-template and sure enough there is. You can nest template elements. So I tried something like this:

<masonry :cols="{default: 2, 700: 1}" :gutter="30">
    <template v-for="number in [1,2,3,4,5,6,7]">
        <basic-card :key="number"/>
    </template>
</masonry>

and sure enough, everything seems to be working! Hope this helps.

tcober avatar Dec 12 '18 18:12 tcober

@tcober Seems like a useful workaround, however I'm also using a draggable component to my implementation, which means I can't get the required root-level structure :(

roberttolton avatar Dec 13 '18 23:12 roberttolton

I'm having issues when I wrap the elements in tags to give them entrance animations. The initially rendered ones won't animate and this causes the lazy-loaded images to not render unless I modify the array.

imagen

ChibiChanNya avatar Jan 08 '19 22:01 ChibiChanNya