Volto: apiExpanders and redux storage [Solved]

Hey community,

Following the following documentation: 4. Writing a content expansion endpoint – Effective Volto – Backend — Plone Training 2024 documentation

I have configured in my backend addon a new restapi endpoint called @header_info
And tell Volto about it:

  config.settings.apiExpanders = [
    ...config.settings.apiExpanders,
    {
      match: '',
      GET_CONTENT: ['header_info'],
    },
  ];

So far so good, now in each request to the backend from Volto I can see the header_info in the URL, and the info in the response:

++api++/en/folder?expand=translations,breadcrumbs,actions,navroot,navigation,header_info

However when I try to get the information from redux storage with useSelector hook, the info is not there:

  const headerImage = useSelector((state) => {
      console.log(state);
      return state?.header_info
   });

What extra step I'm missing?

What extra step I'm missing?

Ok, it was not that hard :grinning:.
Looks like I need my own action/reducers and dispatch the action in my component. I borrowed most of the code from breadcrumbs / navigation

For reference:

In my Component.jsx

import { getHeaderInfo } from '../../actions';

[...]
  useEffect(() => {
    if (!hasApiExpander('header_info', getBaseUrl(pathname))) {
      dispatch(getHeaderInfo(getBaseUrl(pathname)));
    }
  }, [dispatch, pathname]);

Action:

/**
 * Header Info actions.
 * @module actions/header_info/header_info
 */

import { GET_HEADER_INFO } from '../../constants/ActionTypes';

/**
 * Get Header Info.
 * @function getHeaderInfo
 * @param {string} url Content url.
 * @returns {Object} Get header info action.
 */
export function getHeaderInfo(url) {
  return {
    type: GET_HEADER_INFO,
    request: {
      op: 'get',
      path: `${url}/@header_info`,
    },
  };
}

Reducer:

/**
 * Header info reducer.
 * @module reducers/header_info/header_info
 */

import { map } from 'lodash';
import {
  flattenToAppURL,
  getBaseUrl,
  hasApiExpander,
} from '@plone/volto/helpers';

import { GET_HEADER_INFO } from '../../constants/ActionTypes';
import { GET_CONTENT } from '@plone/volto/constants/ActionTypes';

const initialState = {
  error: null,
  loaded: false,
  loading: false,
  header_info: null
};

/**
 * Header info reducer.
 * @function header_info
 * @param {Object} state Current state.
 * @param {Object} action Action to be handled.
 * @returns {Object} New state.
 */
export default function header_info(state = initialState, action = {}) {
  let hasExpander;
  switch (action.type) {
    case `${GET_HEADER_INFO}_PENDING`:
      return {
        ...state,
        error: null,
        loaded: false,
        loading: true,
      };
    case `${GET_CONTENT}_SUCCESS`:
      hasExpander = hasApiExpander(
        'header_info',
        getBaseUrl(flattenToAppURL(action.result['@id'])),
      );
      if (hasExpander && !action.subrequest) {
        return {
          ...state,
          error: null,
          loaded: true,
          loading: false,
          header_info: action.result['@components'].header_info,
        };
      }
      return state;
    case `${GET_HEADER_INFO}_SUCCESS`:
      hasExpander = hasApiExpander(
        'header_info',
        getBaseUrl(flattenToAppURL(action.result['@id'])),
      );
      if (!hasExpander) {
        return {
          ...state,
          error: null,
          loaded: true,
          loading: false,
          header_info: action.result['@components'].header_info,
        };
      }
      return state;
    case `${GET_HEADER_INFO}_FAIL`:
      return {
        ...state,
        error: action.error,
        loaded: false,
        loading: false,
      };
    default:
      return state;
  }
}