First - this is really great project!
I’m new here and I hope I’m describing my question clear is possible:
I build two “widget like” components that will display posts from two different categories.
My goal is the fetch category X and only when X is ready (.isReady) fetch category Y
If I understand it right; the best place to this is on the root index.js under actions: using afterCSR/beforeCSR etc…
I don’t understand well why do you need to make the fetch to '/post_format/gallery/' when dataNBA.isReady. As frontity access the REST API, data from both endpoints should be available anytime
Maybe the names of the methods are confusing you. These methods belong to the wp-source package that is documented in the docs
fetch is an action available though the wp-source package that fetches all entities related to a link. This means, this method will actually perform a request and will put the information retrieved in the state
This method returns a promise, so when the promise is resolved the data will be fully available in the state.
The property isReady available for each link info (that can be get through state.source.get()) informs when the request triggered by actions.source.fetch() has finished and the data is available in the state
This is the method that allow us to read data available for each link from the state. As this method returns final data (titles, contents, author names…) most of the times it will be used directly from the React component that is going to display that data.
So, if you have created a widget component in React, maybe this logic can be directly part of that component (in the useEffect hook for example) because by using afterCSR all the pages loaded by Frontity (even those that are not using that widget) will execute that logic. If you put the logic in the widget component, the logic (fetching data and so) will be executed only when the component is used
In case you want to learn more about how the fetch action and get method works along with the state manager here you have some videos where the DevRel team talks about this
This may a little confusing.
Looking at this docs: https://docs.frontity.org/api-reference-1/wordpress-source#how-to-use (very similar what I need)
I’ve understand that: First I need to Fetch the data from the category and then use state.source.get
Current me if I wrong …
I’m using afterCSR because I was unable to create a dynamic component that loads my data by order.
Lets say I have created this component:
const Widget1 = ({ categorySlug, state, actions }) => {
useEffect(() => {
actions.source.fetch(categorySlug);
}, []);
const data = state.source.get(categorySlug);
if (data.isReady) {
// ... do stuff
}
One key idea in Frontity is that the source of data is the state. And React components that are connected to the state, can “react” (they will be re-rendered with latest data) to updates in that state.
All the logic regarding data is handled by the wp-source package.
In the docs you can find the following example with comments that I think it can help you to understand better how this works
import React, { useEffect } from "react";
import { connect } from "frontity";
// In a React component that uses "connect":
const CategoryNature = ({ state, actions }) => {
// 1. fetch data related to a path
// With this useEffect we make the call to fetch
// only the first time the component is rendered.
// When the data is fetched, the state is updated with the new data
// so the component is re-rendered and "data" will get proper content
useEffect(() => {
actions.source.fetch("/category/nature/");
}, []);
// 2. get data from frontity state
const data = state.source.get("/category/nature/");
// 3. get entities from frontity state
if (data.isCategory) {
// the category entity
const category = state.source.category[data.id];
// posts from that category
const posts = data.items.map(({ type, id }) => state.source[type][id]);
// 4. render!
return (
<>
<h1>{category.name}</h1>
{posts.map((p) => (
<a href={p.link}>{p.title.rendered}</a>
))}
</>
);
}
return null;
};
export default connect(CategoryNature);
The key idea here is that when the actions.source.fetch finishes, the component is re-rendered with updated information, so the state.source.get method will be able to get data form the actions.source.fetch
This approach (using the state as the source of truth) have several advantages:
The same data is used across all your React components
This state can be used in both SSR and CSR versions of your pages and components
Whenever the state is updated with new information all components connected (by using connect HOC) to the state will be re-rendered
I still don’t understand why do you need to load one category after the other as they are independent categories that can be loaded simultaneously, and whenever the data is available in the state, each <Widget1> component will re-render accordingly
The main reason I want to load categories depend on each other is because sometimes “boxing” category loads/rendering before “nba” (when user reloading the page/first time visit)
This is interpret for my smooth HP widgets load and give the user poor experience …
Imagine you have 3 boxes of <Widget1> one below the other, and the 3th widget loads before the 1st…
I want while user scroller down, the widget (categories) will load by their order
// 1st on to show:
<Widget1 categorySlug="/category/nba/" />
// 2nd on to show:
<Widget1 categorySlug="/category/boxing/" />
// 3rd on the show:
<Widget1 categorySlug="/category/football/" />
// etc...
If that case the widget component is not meant to be “independent” so I guess it makes sense the widget is not in charge of making the requests. It can be just a “dumb” component displaying the info it receives
You could do something like this to assure the widgets are only rendered when the information is available
import React, { useEffect } from "react";
import { connect } from "frontity";
const BlockWithWidgets = ({ state, actions }) => {
// 1. we make all the requests at the same time.
useEffect(() => {
Promise.all(
[
actions.source.fetch("/category/nba/").
actions.source.fetch("/category/boxing/"),
actions.source.fetch("/category/football/")
]
)
}, []);
// 2. get data from frontity state
const dataNba = state.source.get("/category/nba/");
const dataBoxing = state.source.get("/category/boxing/");
const dataFootball = state.source.get("/category/football/");
let categoryNba, categoryBoxing, categoryFootball
let postsNba, postsBoxing, categoryFootball, postsFootball
// 3. get entities from frontity state
if (dataNba.isReady && dataNba.isCategory) {
const categoryNba = state.source.category[dataNba.id];
const postsNba = dataNba.items.map(({ type, id }) => state.source[type][id]);
}
if (dataBoxing.isReady && dataBoxing.isCategory) {
const categoryBoxing = state.source.category[dataBoxing.id];
const postsBoxing = dataBoxing.items.map(({ type, id }) => state.source[type][id]);
}
if (dataFootball.isReady && dataFootball.isCategory) {
const categoryFootball = state.source.category[dataFootball.id];
const postsFootball = dataFootball.items.map(({ type, id }) => state.source[type][id]);
}
if (categoryNba && categoryBoxing && categoryFootball) {
// 4. only render widgets when all the ingormation is available
return (
<>
<Widget1 category={categoryNba} posts={postsNba} />
<Widget1 category={categoryBoxing} posts={postsBoxing} />
<Widget1 category={categoryFootball} posts={postsFootball} />
</>
);
}
return null;
};
export default connect(CategoryNature);
this logic will be activated on the Client Side. I you want this info to be available from SSR (you’ll need to move the logic from the useEffect to the beforeSSR action
Hi @juanma. This was a great explanation. And it really works well.
I just wanted to know others that if you fetch these data in beforeSSR then it will increase the initial load time of the page. So the better idea is to use this inside useEffect so that it can fetch APIs once the initial page loading complete.