Add posts to state.source.post

hi

I’ll probably speak to the wind again but I’m kinda desperate

I’ve managed to fetch some posts using handler, add it to state.source.data and state.source.post, but I can’t loop to display them because the posts returned by handlers does not contain post id, just basic index. How can i add post id into post object name like frontity does with basics posts?

eg like this
from this :

0: Object { id: 164783, date: "2022-01-03T10:54:11", date_gmt: "2022-01-03T09:54:11", … }

to this

239106: Object { id: 239106, date: "2022-03-30T15:07:57", date_gmt: "2022-03-30T13:07:57", … }

here is a console.log of my state.source.post
items

here is the piece of code where I need it

          <SimpleGrid columns={{ base: 1, md: 2 }} spacing="5px">
            {filterPost
              // .filter((el) => el.id[authorByUrl])
              .map(({ type, id }) => {
                const item = state.source[type][id];
                return (
                  <ArchiveItem
                    isAuthor={data.isAuthor}
                    key={item.id}
                    item={item}
                  />
                );
              })}
          </SimpleGrid>

I know the map failed because it can’t access Id from object name

thanks for your help

Hi @1600tc,

Would be good to get some more info about this? Usually frontity has out of the box functionality for fetching posts. Do you need a special handler for it?

I would love to help you but I need more info. Could you also just show us the handler?

Greetings,
Nikolay

Hi, glad to see somebody else ! In the meantime i’ve found a solution, it works but it might not be the best, see my handler.

I’m working on some kind of newspaper where some company can make an account and pay the newspaper to sponsor some post (this way the newspaper does not rely on annoying ads) and I need to display a list of sponsored posts in the user page that payed for these posts.

To link some users to certain posts in wp i’ve used a relation acf fields, so when journalist create posts it can choose a users in a dropdown list. The selected user then appear in the acf field of that post. I’ve added some code in wp to be able to filter these posts and return them. After that I’ve created a custom handler to get those fields, and managed to find a way to loop trough them but it does not work very well, for some reasons it does not retrieve the featured_media of sponsored posts

const EntrepriseHandler = {
  name: "EntrepriseMeta",
  priority: 5,
  pattern: "posts",

  func: async ({ route, params, state, libraries, link }) => {
    const { api } = libraries.source;
    console.log("entreprise", route);
    // 1. fetch the data you want from the endpoint page
    const response = await api.get({
      endpoint: "posts",
      params: {

        //per_page: 20, // To make sure you get all of them
        meta_key: "nom_enteprise"
      },
    });

    // 2. get an array with each item in json format
    const items = await response.json();
    const reducedPost= items.reduce((acc, item) => ({ ...acc, [item.id]: item }), {})

    // 3. add data to source
    const addPost = state.source.post
    //state.source.post[link].isEntreprise = true;
    Object.assign(addPost, {
      reducedPost,
    });
  },
};

export default EntrepriseHandler;

This is a heavily modified Chakra Ui theme. With the .reduce() method it works, I’m able to loop trough that post list (and posts are returned in the state as objects rather than array of objects)

But for some reasons featured_media returned by the formatPostData() methods (from the theme) does not return featured_media. Maybe it’s because reduce methods is not the best approach ?
here is the updated part of archive-page.js (also using infinite scroll if that matters)

  const entreprise = state.source.post.reducedPost
  const entrepriseArray = Object.values(entreprise)
  return (
    <Box as="section"  >
      {/* display taxonomy page*/}
      {data.isTaxonomy && data.isCategory && notShow && (
       (...)
      )}
      {data.isTag && <TagHeader state={state} />}
       (...)
      <Box
        padding={{ base: "17px 14px 14px 14px", lg: "40px" }}
        bg={checkBg(data)}
        width={{ lg: "80%" }}
        maxWidth="1200px"
        mx="auto"
        h="100%"
      >
        {/* Iterate over the items of the list. */}

        {isStoriesClicked === "parrain" ? (
          <SimpleGrid columns={{ base: 1, md: 2 }} spacing="5px">
            {entrepriseArray
              //.filter((el) => el.id[authorByUrl])
              .map(({ type, id }) => {
                const item = state.source.post.toast[id];
                return (
                  <ArchiveItem
                    isAuthor={data.isAuthor}
                    key={item.id}
                    item={item}
                   
                  />
                );
              })}
          </SimpleGrid>
        )

Thanks for your help !!

Also here is what my state.source.post looks like

Okey, I understand what you have done. I honestly don’t think you need a custom handler for this as with the default handler you can pass params. Here is how I would approach it:

  1. Make an action that fetches posts and receives params and a callback function.
    For example:
    fetchPostsWithCallback:
      ({ state, libraries }) =>
      async (prms, callback) => {
        // Get posts
        const response = await libraries.source.api.get({
          endpoint: "posts",
          params: prms,
        });

        const entitiesAdded = await libraries.source.populate({
          response,
          state,
        });
        callback && callback(entitiesAdded);
      },
  1. Then in your view, where you need those specific posts you can do something like this:
const SomeComponent = () => {
  
  const [items, setItems] = useState([]);

  useEffect(() => {
      actions.theme.fetchPostsWithCallback({
        // Here come your filtering params, based on how you have made your filtering work in wp
      }, setItems);
  }, []);
  
  return items.length ? (
    <div>
      {items.map(item => {
        const post = state.source[item.type][item.id]
        return (
          <PostComponent post={post}/>
        )
      })}
    </div>
  ) : null

}

As you can see we are passing setItems as the callback. That means that the items that are populated in the state by your action will get stored in your local state items. So that means that your component now knows which specific posts it should render as we can assume you will also have other posts in state.source.posts.

If you want to have a loading state, you can adjust the return value in the case when items is empty to your loading component.

Thats how I usually handle those types of cases. I don’t know if its the best way, but its the best I have found as I have faced this case many times already. This also allows you to render the posts in your front-end without doing any actual filtering in the front-end.

I hope that helps. Let me know if I can clarify something or give some extra info.

Oooh okay that looks better !

I will try tomorrow, thanks !

hi ,

your code works nicely and the page seems to be loading faster and avoid a fetch in beforeSSR, nice :+1:

however my post image does not render at all, I can loop trough my posts previews, it has date, title , everything except featured media (which is supposed to be a picture)
The posts are passed in formatPostdata function (chakra ui theme functions) and for some reasons it does not “see” the post featured media even thought it is in the post state.
Have you ever encountered something like this ?

You can include the param _embed from WP rest api. Global Parameters | REST API Handbook | WordPress Developer Resources
I believe that if you include it, frontity will automatically populate state.source.media with the media from the post.

Thanks again for your help !
Should I add this like that ? How can I check if thats works ? Because like that it does not solve my issue

  useEffect(() => {
      actions.theme.fetchPostsWithCallback({
        meta_key: "nom_enteprise",
        embed:"?_embed"
      }, setItems);
  }, []);

I believe the value of embed has to simply be true. You can check the wordpress api docs link i sent above. So I believe it should be:

  useEffect(() => {
      actions.theme.fetchPostsWithCallback({
        meta_key: "nom_enteprise",
        _embed: true
      }, setItems);
  }, []);

Yeah, i’ve read it after my reply, sorry.

It actually work, thanks for your help you are awesome :+1:

Glad I could help.