How to toggle className

My objective seems quite simple, but I can’t manage to wrap my head around how Frontity and React work in this respect. I’ve looked around in the Frontity Community, in Mars-theme and on React documentation.

In the React documentation I’ve found various examples, but there seem to be differences between Frontity components and React (do I need to rewrite a component to class xxx extends React.Component for this?). At least, it looks that way, with my pretty limited React and Frontity skills.

What I’ve done so far:
I have added my own nav menu’s, coming from the WordPress REST API (I used this topic: How to fetch Menu from wordpress?). I also added child-items for the menu-items, because only the top-level is not enough. On desktop it’s fine if these child-items appear when hovering on their parents. But on touchscreens hovering doesn’t work, so I’d like to add toggles to show and hide the child-items, at least for smartphone sized devices.

The easiest way seems (to me) to toggle a class-name on the toggle-element and add some CSS to show the child-items only when the active class on the toggle is added. I’ve added a component which should work as a toggle and tried several ways, but I’m unable to add the class after an onClick event.

I have also tried to do this through state.theme, but because there could be multiple toggles in the menu, this doesn’t seem to be the right approach to me.

This is my component, cleared from all my trial & error code:

import React from "react";
import { css, connect } from "frontity";

const NavDropdownToggle = ({
  state,
  actions,
}) => {
  return (
    <span css={css`margin-left: 10px;`}>
      toggle
    </span>
  )
};

export default connect(NavDropdownToggle);

What’s the right method to create a function to toggle a class on this element?

It feels like I’m almost there with the approach I chose, but it’s not completely working for me.

I created this component:

import React from "react";
import { css, connect } from "frontity";

const NavDropdownToggle = ({
  state,
  actions,
  theme,
  target,
}) => {
  let isSubmenuOpen = state.theme.isSubmenuOpen[target];

  isSubmenuOpen == null && actions.theme.toggleSubmenu[target];

  return (
    <span css={css`margin-left: 10px;`} submenu-open={isSubmenuOpen} onClick={actions.theme.toggleSubmenu[target]}>
      toggle
    </span>
  )
};

export default connect(NavDropdownToggle);

And this is the actions part of index.js:

actions: {
  theme: {
    ...
    toggleSubmenu: ({ target, state }) => {
        state.theme.isSubmenuOpen[target] = (state.theme.isSubmenuOpen[target] == 'open') ? 'closed' : 'open';
    }
  },
},

I don’t know how to pass an argument to actions.theme.toggleSubmenu. The target does have a value, but when using actions.theme.toggleSubmenu(target) I get an error Too many re-renders. React limits the number of renders to prevent an infinite loop. and when using actions.theme.toggleSubmenu[target] the action doesn’t run.

Solved it, trialling and erroring my way through…

I removed the state.action and modified the component to this:

import React from "react";
import { css, connect } from "frontity";

const NavDropdownToggle = ({
  state,
  actions,
  theme,
  targetid,
}) => {
  state.theme.isSubmenuOpen[targetid] = (state.theme.isSubmenuOpen[targetid] == null) ? 'closed' : state.theme.isSubmenuOpen[targetid];
  console.log(targetid);

  const toggleSubmenu = ( targetid ) => {
    state.theme.isSubmenuOpen[targetid] = (state.theme.isSubmenuOpen[targetid] == 'closed') ? 'open' : 'closed';
  }

  return (
    <span css={css`margin-left: 10px;`} submenu-open={state.theme.isSubmenuOpen[targetid]} onClick={() => toggleSubmenu(targetid)}>
      toggle
    </span>
  )
};

export default connect(NavDropdownToggle);

The only thing I need in my index.js is the following:

const marsTheme = {
  ...
  state: {
    theme: {
      ...
      isSubmenuOpen: {},
      ...
    },
  },
  ...
};

export default marsTheme;

I’m not confident that this is the best approach, but at least it’s working.

Hi @dominique

Great that you eventually got to a solution. Regarding your query about class-based React components, there used to be an advantage to using them in order to use setState, but since React 16.8 you can use React hooks in functional components. I don’t think you need to use the class syntax for declaring components in a Frontity theme. See this brief article: https://medium.com/@Zwenza/functional-vs-class-components-in-react-231e3fbd7108