Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List is rerendered on browser even with zero state changes #1285

Closed
StarpTech opened this issue Mar 13, 2019 · 6 comments
Closed

List is rerendered on browser even with zero state changes #1285

StarpTech opened this issue Mar 13, 2019 · 6 comments

Comments

@StarpTech
Copy link
Contributor

StarpTech commented Mar 13, 2019

Marko Version: 4.15.2

My components looks as follow:

class {}
style.scss {
  .category-components {
    .breadcrumb {
      padding-left: 20px;
      font-size: 12px;
    }
  }
  .product-list {
    margin-top: 30px;
    &__item {
      &__image {
        padding: 20px;
        background-color: #eaeaea;
        width: 100%;
        height: 100%;
      }
      &__details {
        margin: 20px 0;
      }
    }
  }
}

<section class="category-components">
  <section class="product-list container">
    <div class="row">
      <for|product| of=input.products>
        <div class="col-4">
          <div class="product-list__item align-middle">
            <a href=`/components/${input.category.slug}/${product.slug}`>
              <div class="product-list__item__image d-flex align-items-center justify-content-center text-center">
                <img src="../../global-asset/images/product-example.png"/>
              </div>
            </a>
            <div class="product-list__item__details">
              <span class="product-list__item__name d-block font-weight-bold">
                <a href=`/components/${input.category.slug}/${product.slug}`>${product.name}</a>
              </span>
              <span class="product-list__item__name d-block">${product.type}</span>
              <span class="product-list__item__name d-block">${product.vendor}</span>
            </div>
          </div>
        </div>
      </for>
    </div>
  </section>
</section>

Expected Behavior

The component should be rendered only on server-side.

Actual Behavior

The component is rerendered on the client even when no state was changed.

Workaround

Remove the complete class or don't use dynamic attribute tags for the layout.

Environment

I'm using layout tags and the default marko & lasso setup.

Ubuntu 18.04

@DylanPiercey
Copy link
Contributor

The problem here (as you've discovered) is that if a component has an inline class or component.js file then it is determined to be a stateful component and must be rerendered in the browser.

Using class as the heuristic isn't ideal but it's the best we've got at the moment. If you'd like to use the components lifecycle methods you can add a component-browser.js file which supports a limited portion of the Marko lifecycle and runtime API and allows Marko to skip sending down this component (unless it is under another component). More info here.

Let me know if this solves your issue.

@StarpTech
Copy link
Contributor Author

StarpTech commented Mar 13, 2019

No success the payload is still delivered to the browser.

component-browser.js

module.exports = {
  onFilterChange(filterFormState) {
    console.log(filterFormState);
  }
};

index.marko

<section class="category-components">
  <section class="product-list container">
    <div class="row">
      <for|product| of=input.products>
        <div class="col-4">
          <div class="product-list__item align-middle">
            <a href=`/components/${input.category.slug}/${product.slug}`>
              <div class="product-list__item__image d-flex align-items-center justify-content-center text-center">
                <img src="../../global-asset/images/product-example.png"/>
              </div>
            </a>
            <div class="product-list__item__details">
              <span class="product-list__item__name d-block font-weight-bold">
                <a href=`/components/${input.category.slug}/${product.slug}`>${product.name}</a>
              </span>
              <span class="product-list__item__name d-block">${product.type}</span>
              <span class="product-list__item__name d-block">${product.vendor}</span>
            </div>
          </div>
        </div>
      </for>
    </div>
  </section>
</section>

Could you point me to the place where that decision is made?

@DylanPiercey
Copy link
Contributor

The way that it works is that the Marko compiler exposes some meta data that has info like is there a component-browser and a list of browser dependencies. It's up to the bundler implementation to actually load the right assets in the browser. You can see how we are doing this in lasso here.

Having said that the above does not look like the template should be sent down. Is there anything else in the template? Are you using a browser.json or anything like that?

@StarpTech
Copy link
Contributor Author

StarpTech commented Mar 13, 2019

Yes, I use a browser.json on page level but it only includes jquery and some sass files. The really strange is: When I remove everything in the <@footer> tag in my root page, the code isn't serialized. The code in the footer container is:

  <@footer>
    <section class="container">
      <app-footer/>
    </section>
  </@footer>

and app-footer is a marko file with 2 divs no js. It is worse than that, when I remove the whole @footer it is serialized again! Please don't tell me that marko tags or lasso tags force the bundler to build for the browser.

@StarpTech
Copy link
Contributor Author

StarpTech commented Mar 14, 2019

The bug is still there and looks like a deeper marko issue of dynamic attribute tags You can reproduce it as follow:

  • Prepare a setup as described in the issue
  • Create basic page with head, body and footer (like in the playground)
  • Insert a component inside the body tag
  • Pass some data to that component
  • Any data you will pass to that component will be serialized to the browser The component isn't stateful or is a child of a stateful component. The split in separate files has no effect. This results in that the component is marked as client side and therefore my template is rerendered on client site.

@DylanPiercey
Copy link
Contributor

Although this is something we plan to automatically optimize away in the future we've mentioned this caveat in the docs (https://markojs.com/docs/server-side-rendering/#caveats).

Also using split-components allows for some additional optimization, and you can manually prune input for split components by following https://markojs.com/docs/server-side-rendering/#serialization

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants