Source code disappears on using beforeSSR

    theme: {
      toggleMobileMenu: ({ state }) => {
        state.theme.isMobileMenuOpen = !state.theme.isMobileMenuOpen;
      },
      closeMobileMenu: ({ state }) => {
        state.theme.isMobileMenuOpen = false;
      },
      beforeSSR: async ({ state, actions }) => {
        await Promise.all([
          actions.source.fetch("/category/featured/"),
          actions.source.fetch("/category/how-to/"),
          actions.source.fetch("/category/accessories/"),
          actions.source.fetch("/category/iphone/"),
          actions.source.fetch("/category/iphone/page/2"),
        ]);
      },
    },
  },

I need to display posts from five different categories on the homepage. Everything renders well, except source code of homepage is incomplete and doesn’t show any fetched data. Is their a way to get full source code of the homepage?

Hi @sarang,

Please take a look at this example where you can see how to do exactly that

With the piece of code you provide you’re only assuring that those requests are made and that all necessary data you need it’s available at state.source

What that data then you have to create the proper component to display that information. Here you have an example

Hope this helps!

What are you building with Frontity?

Hi @juanma thanks for your response. Actually, I didn’t to a good job explaining the problem.

I have used the example that you mentioned => https://github.com/frontity-demos/demo-custom-homepage-categories

It works well, I receive the data and page is rendered as expected and required. But, it seems I am fetching a lot of data that I don’t need. Can I reduce the number of posts that are fetched from 10 to say 5 and also remove the key-value pairs in the post object that I don’t need?

Because I receive too much data, it slows down the app and also takes a long time to load source code(View Page Source).

Hi @sarang,

I think you’re getting much more data in the REST API because of some plugins you may have installed (try to deactivate your plugins and check the size of your REST API)

What you can do to control the amount of data received from the server is creating custom endpoints in your Wordpress installation and then, creating custom handlers in Frontity to access the data provided in these custom endpoints

A simpler approach (no creation of custom endpoints) is to just create custom handlers in Frontity and use the _fields to specify the fields you want to receive from the REST API endpoint in your handler

If you still need more support from the community, we’d need that you provide, so you can get proper answers:

  • A lighthouse report showing the performance of your site
  • A URL of a deployed version of your project so the community can verify the amount of data received from the WP REST API
  • A Github repo so the community can check the code you’re actually using

The more info we have the more accurate the community’s response.

Thanks for sharing your doubts and help the community grow.

BTW… What are you building with Frontity? :blush:

3 Likes

Hi @juanma thanks for replying. We are moving a Wordpress website with over 5k articles to Frontity. More info here:

Additionally, _fields param seems quite useful, thanks for the tip. I get how to use it in a custom handler. But, is there a way to use _fields with Frontity’s handler (beforeSSR method)? It will probably save me some time.

beforeSSR: async ({ state, actions }) => {
  await Promise.all(
     Object.values(beforeSSRCategories).map((category) => {
            return actions.source.fetch(`/category/${category.name}/`);
     })
  );
}

BTW heres my current custom handler (could have been lot simpler if I had used _fields ):

const homeCustomHandler = {
  name: "homeCustomHandler",
  priority: 1,
  pattern: "home-custom-handler",
  func: async ({ route, state, libraries }) => {
    const categoriesId = Object.keys(homePageCategories);
    const t0 = new Date().getTime();
    let posts = [];
    try {
      await Promise.all(
        categoriesId.map((id) => {
          return libraries.source.api
            .get({
              endpoint: `/wp/v2/posts?categories=${id}&per_page=${homePageCategories[id].posts}`,
            })
            .then((response) => {
              return response.json();
            })
            .then((responsePosts) => {
              let categoryPosts = responsePosts.map((post) => {
                return {
                  id: post.id,
                  slug: post.slug,
                  date: post.date_gmt,
                  authorId: post.author,
                  title: post.title.rendered,
                  content: post.content.rendered,
                  category: homePageCategories[id].name,
                  featured_mediaId: post.featured_media,
                  article: homePageCategories[id].article,
                };
              });
              posts.push(...categoryPosts);
              return responsePosts;
            })
            .catch((err) => console.log(err));
        })
      );

      const endpoints = [];
      posts.forEach((post) => {
        endpoints.push([`/wp/v2/media/${post.featured_mediaId}`, post]);
        endpoints.push([`/wp/v2/users/${post.authorId}`, post]);
      });

      await Promise.all(
        endpoints.map((endpoint) => {
          return libraries.source.api
            .get({
              endpoint: endpoint[0],
            })
            .then((response) => {
              return response.json();
            })
            .then((responseJSON) => {
              if (responseJSON.source_url) {
                const postSrc = responseJSON.guid.rendered;
                let postSrcSet = [];
                for (let [key, value] of Object.entries(responseJSON.media_details.sizes)) {
                  postSrcSet.push([value.source_url, value.width]);
                }
                endpoint[1]["src"] = postSrc;
                endpoint[1]["srcSet"] = postSrcSet;
              } else {
                endpoint[1]["author"] = responseJSON.name;
              }
            })
            .catch((err) => console.log(err));
        })
      );

      const t1 = new Date().getTime();
      console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
    } catch (error) {
      console.log(error);
    }

    const data = state.source.get(route);
    Object.assign(data, { posts });
  },
};

Hi @sarang

I had a chat with our dev team and it seems that it’s currently not possible to use _fields with Frontity’s handler. It is something that they are working on and they hope to have that functionality in v2 of wp-source.

If your current custom handler is doing what you want it to, then it’s probably best to run with that for now.

Do let us know if we can help you further.

2 Likes

Thanks @mburridge for letting me know. :slightly_smiling_face:

Sorry if I am bothering too much, but when to expect v2 of wp-source ?

Hi @sarang

Sorry, we don’t currently have a timeline for that so we’re not sure when v2 will be ready. We’ll endeavour to keep you updated on progress here.

1 Like

@mburridge @juanma

I am fetching a lot of posts on homepage, so it makes sense that source code for homepage is huge. But, on clicking a post and navigating post page, source code for post page is also huge and almost the same size as homepage. Shouldn’t it be a lot smaller as it contains data for only one post?

live URL (click only on images) => https://oq-6a2swbjos.now.sh/

I think it’s because you are fetching all the categories unconditionally in the beforeSSR action, not only for the home:

beforeSSR: async ({ state, actions }) => {
  await Promise.all(
    Object.values(beforeSSRCategories).map((category) => {
      return actions.source.fetch(`/category/${category.name}/`);
    })
  );
};

Right now, you have to do the conditional logic yourself to avoid that. For example, if you want to do this fetch only on the home, you can:

beforeSSR: async ({ state, actions }) => {
  if (state.router.link === "/")
    await Promise.all(
      Object.values(beforeSSRCategories).map((category) => {
        return actions.source.fetch(`/category/${category.name}/`);
      })
    );
};

If you want to do this also on categories, you can:

beforeSSR: async ({ state, actions }) => {
  if (state.router.link === "/" || state.router.link.startsWith("/category/"))
    await Promise.all(
      Object.values(beforeSSRCategories).map((category) => {
        return actions.source.fetch(`/category/${category.name}/`);
      })
    );
};

And so on.

Once you do this conditionally, the data won’t be there when you do the server-side rendering of a post and then move to the home, so you also need to include the same logic in a useEffect of those components:

const Home = ({ state }) => {
  useEffect(() => {
    Object.values(fetchCategories).map((category) => {
      actions.source.fetch(`/category/${category.name}/`);
    });
  });
};

Be aware that you have to check in those components if the fetch has finished or not using data.isReady.


We are aware that this is not the ideal way to do this, so in the v2 of source these types of things are going to be much more easy and flexible because you should be able to attach additional handlers to a certain route/pattern without having to overwrite the original one.

2 Likes