Get a post by ID

Hey everyone,

First off, Frontity is great so far and it’s only getting better the more I understand the concepts.

I’m clearly getting confused with the process of fetching data, however.

I have an ACF field which includes a repeater of references to posts, as well as a background image. I’ve been able to get the data for the background image without issues, but now I want to get the data from the referenced post.

I thought I could fetch the data by doing:
const data = state.source.post[sample.ID]; (where sample.ID is a number like 51 — I’ve confirmed that the document ID is correct)

However when I log this out I get undefined…

You can see the full source here:

What am I doing wrong?

Clearly missing something here as I’m running into the same trouble with another component.

I’ve read through the documentation on state over and over again but clearly I’m missing something. Hopefully someone can help as I’m really finding progress to be slow trying to work this stuff out.

Hi @modg

If the data is not already in the state then you will get undefined. Frontity normally fetches data using URLs constructed using the post slug, basically the same way that WordPress uses permalinks.

See this thread which also discusses this topic.

You will need to use libraries.source.api.get to fetch the specific post using an endpoint such as http://www.domain.com/wp-json/wp/v2/posts/33.

You will then also need to add the retrieved data into the state using libraries.source.populate, as libraries.source.api.get doesn’t automatically populate the state in the way that actions.source.fetch does.

Thanks so much for the reply @mburridge. I’m due to work on this project again tomorrow so I’ll be able to take a proper look then.

But in theory, and so I have it straight using my first link as an example, from this component I would use libraries.source.api.get() to fetch all of the pages in the loop, something like:

{slide.samples[0].sample.map((sample) => {
  const data = state.source.post[sample.ID];
  const samplesData = await api.get({
    endpoint: "posts",
    params: { _embed: true, posts: sample.ID },
  });

  // return...
})}

Or would I do that before the loop, add the retrieved data to the state using libraries.source.populate() and then retrieve the data from the state in my loop?

Hope I’m understanding this right and that my reply is clear. Thanks again!

Hi @modg

I’m not sure of the structure of your data or what you’re looping over, but yes, the data needs to be fetched and the state populated with the fetched data before you use state.source.post[id].

If doing something like const data = state.source.post[sample.ID]; add some error checking code to ensure that data is not undefined before attempting to use it.

Hey @mburridge — apologies, I’m still not getting this… :tired_face:

Here is my component. The id prop returns an ID for a page, like 52 or 12, etc.

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'frontity';

const Wayfinder = ({ id, state }) => {
  if (!id) return null;

  return (
    <div>
      {id}
      <br />
    </div>
  );
};

export default connect(Wayfinder);

All I want to do is fetch the page data from any given ID to this component and have access to its content like featured image, title, acf fields, etc.

Hi @modg

I’ve cloned your repo locally, and I can see what’s being returned from your API at http://167.99.197.102/wp-json/wp/v2/posts.

I’m not sure what I should be looking for. Which is the ACF field that contains the ID of the post that you want to fetch?

Why not use useEffect to retrieve the post content before rendering it?

import React, { useEffect } from "react";
import PropTypes from 'prop-types';
import { connect } from 'frontity';

const Wayfinder = ({ id, state, actions }) => {
  if (!id) return null;

useEffect(() => {
    actions.source.fetch("/posts/" + id);
  }, []);

  const data = state.source.get("/posts/" + id);
  const post = state.source.post[id];

  return (
    <div>
      <a href={post.link}>{post.title.rendered}</a>
      <br />
    </div>
  );
};

export default connect(Wayfinder);

Thanks @mburridge. The ID linking to the page I’m looking to return can be found by doing console.log(id) in the Wayfinder.js file. That comes from another component called ContentBlocks.js. This component loops through a ‘flexible content’ field in ACF which can contain different blocks — it’s a bit of a ‘page builder’ component I wrote.

Ultimately though the id is passed to Wayfinder.js so logging the id prop should return a number.

I actually did try something like this using the await-to-js library;

  const [post, setPost] = useState(null);

  useEffect(() => {
    async function getPostById() {
      const { api } = libraries.source;
      let err;
      let res;
      let resData;

      [err, res] = await to(
        api.get({
          endpoint: `pages/${id}`,
          params: {
            _embed: true,
          },
        })
      );
      if (!res) {
        console.error(
          'No response returned from get. Please check the endpoint is correct'
        );
        console.error(err);
      }

      [err, resData] = await to(res.json());
      if (!resData) {
        console.error(
          'Error getting JSON from res. Please check the endpoint is returning a proper JSON'
        );
        console.error(err);
      }

      setPost(resData);
    }

    getPostById();
  }, []);