List all categories as links

Hi all,
I want to create a menu based on my categories from WP http://somedomain.com/wp-json/wp/v2/categories
What should I do? Create custom page and list all categories? Can I access to this data in the “state” and use map() to list it?
It’s not intuitive for me.
any help will be appreciated.

1 Like

I can’t answer your questions but I think that’s something that’s not clear to me either. What all exactly is available from the state? Is there documentation on it? Can we basically access any kind of standard WP-REST API call information via the state?

Hey guys, sorry for the lack of documentation on this aspect, we’ll work on improving it. We try to explain what State is and how Frontity works at this page of the documentation.

State is an object where Frontity exposes all the data needed for the packages. By default, Frontity fetch the common data from WordPress and populate the state.source. This way, you can find all the info about categories, posts, tags, authors, etc. At state.source.data you can access this data using the URL, which makes it really easy.

If you want to access the data of an specific URL you can use state.source.get("/category/nature") as explained at this page of the documentation.

If you want to go deeper on which data is in the state, you can go to your website (or mars.frontity.org), open the dev tools and write in the console frontity.state.

So, going to your specific questions:

Frontity tries to replicate how WordPress works, so most of the information is accessible via state by default, and the recommended way is to get it using state.source.get. However, if you have something particular, you can create your own handler and fetch the endpoint you want.

In relation to the previous questions, this is not an endpoint Frontity fetch from default because it’s not the common use of WordPress, but you can create your own handler to do so. There’s a topic with a pretty similar solution to your case going here. You should do something similar to this.

const allCategoriesHandler = {
  name: "allCategories",
  priority: 10,
  pattern: "all-categories",
  func: async ({ route, params, state, libraries }) => {
    const { api } = libraries.source;

    // 1. fetch the data you want from the endpoint page
    const response = await api.get({
      endpoint: "categories",
      params: {
        per_page: 100 // To make sure you get all of them
      }
    });

    // 2. get an array with each item in json format
    const items = await response.json();

    // 3. add data to source
    const currentPageData = state.source.data[route];

    Object.assign(currentPageData, {
      items
    });
  }
};

const marsTheme = {
  name: "@frontity/mars-theme",
  roots: { ... },
  state: { ... },
  actions: { ... },
  libraries: {
    ...
    source: {
      handlers: [allCategoriesHandler]
    }
  }
};

export default marsTheme;

This way, we have already created our handler. In order to access the data we first have to fetch it using actions.source.fetch("all-categories"), the state would be populated and we can access it with state.source.get("all-categories/").

  1. You may want to have the data accessible from the beginning. If you want to do so, you have to include the fetch also at index.js file. It should be something like this:
const allCategoriesHandler = {
  name: "allCategories",
  priority: 10,
  pattern: "all-categories",
  func: async ({ route, params, state, libraries }) => {
    const { api } = libraries.source;

    // 1. fetch the data you want from the endpoint page
    const response = await api.get({
      endpoint: "categories",
      params: {
        per_page: 100 // To make sure you get all of them
      }
    });

    // 2. get an array with each item in json format
    const items = await response.json();

    // 3. add data to source
    const currentPageData = state.source.data[route];

    Object.assign(currentPageData, {
      items
    });
  }
};

const marsTheme = {
  name: "@frontity/mars-theme",
  roots: { ... },
  state: { ... },
  actions: { 
    ...
    theme: {
      beforeSSR: ({ actions }) => async () => {
        await actions.source.fetch("all-categories");
      }
    }
  },
  libraries: {
    ...
    source: {
      handlers: [allCategoriesHandler]
    }
  }
};

export default marsTheme;
  1. Once you have fetched your categories and they’re available in the state, you just have to consume its data as you want. For example, you could adapt the <Nav /> component of mars-theme:
const Nav = ({ state, libraries }) => {
  const { items } = state.source.data["all-categories/"];

  return (
    <Container>
      {items.map(item => {
        const { name } = item;
        const link = libraries.source.normalize(item.link);
        return (
          <Item key={name} isSelected={state.router.link === link}>
            <Link link={link}>{name}</Link>
          </Item>
        );
      })}
    </Container>
  );
};

export default connect(Nav);

With all of these, you should have your menu with all your categories working, fetching the data from WordPress.

I hope you find this useful and please, let us know if you have any questions :slightly_smiling_face:

Thank you @SantosGuillamot this was super helpful. I have a basic idea of what State is from my intro to React class but this helps me better understand what parts of the WP-API are available through it.

EDIT: I had a question here but I figured it out. I was wondering how I could get the specific link for an author object using frontity.state.source.author.1.link since 1 can’t be passed as part of dot notation. I found that this can be done with frontity.state.source.author[1]link instead!

Is there a specific reason why those keys are numbered instead of given names? I’m guessing it’s because they’re dynamically produced or something right?

I’m glad it helped you :slightly_smiling_face:

We think it’s better to use the id WordPress itself uses for identifying authors, posts, categories, etc. And it’s also recommendable to use unique ids as numbers in these cases.

Btw, in case we’d be using names, you’d still need to use square brackets, as some of them would have spaces for example, and you can’t include that with dot notation. For ex. it would be source.author["Mario Santos"].link (it doesn’t work right now, as you said we work with ids).

Oh, and just to clarify it: I’ve seen you are using frontity.state.author. That’s needed if you are working on browser console, if you are working on your app code you should use just state.author.