Navbar issues in single page app templates when running on port 3000

When I run my project using “npm run dev” and “dotnet watch run” the templates load fine and the new navigation items work fine on localhost:5001.

However, if I run “npm start” and “dotnet watch run” and then open localhost:3000, I get the following error with regard to navigation:

TypeError: undefined is not an object (evaluating ‘state.nav.results’)

This error stems from line 46 in the react-spa template of App.tsx, which is:

I think this is because state.nav is coming from the ServiceStack client library, and maybe the baseUrl needs updated (so in other words, while my react app is running on port 3000, it can retrieve the nav items on port 5001, which currently isn’t happening). I prefer running on port 3000 since the create-react-app has a nice error overlay and hot reloading is faster.

The directions in the readme say it’s okay to run on port 3000, but with the new nav features, is there a way to work around this so I can load my nav items from port 5001 or for the nav items to show somehow?

Also, is there an example on how to customize the nav bar items? I was trying to find this in various source codes you have, but was unable to. Thank you for your help,

Tim

Yeah you wont be able to run from port 3000 as the .html pages needs to be served by ServiceStack not from the webpack dev server, I’ve now removed webpack server option from the docs.

You could run it by replacing all #Script methods, i.e:

With external css references and Ajax calls, but that would be detrimental to performance, requiring several additional network requests for the initial page load.

Thank you, this helps a lot. I’ll use the ServiceStack server as you advise.

Can you point me to either docs or source code on how to customize the navbar items. Do I customize this on the server, or in the client code?

There’s a small snippet in the release notes, as follows:

But with the default choices coming from the server, it seems like there might be a way to customize these on the server side. Thank you.

I think I found where the nav items come from…in appsettings.json. I can then modify in here as needed. So this answers my question from above. But just to clarify, these aren’t modified or stored elsewhere…just the server returns them from the appsettings.json file, correct?

Thanks again, Tim

In the templates they’re initialized from appsettings.json but they can also be populated and further modified from code, e.g:

View.NavItems.AddRange(new List<NavItem>
{
    new NavItem { Href = "/",         Label = "Home",    Exact = true },
    new NavItem { Href = "/about",    Label = "About" },
    new NavItem { Href = "/services", Label = "services" },
    new NavItem
    {
        Href = "/contact", Label = "Contact",
        Children = new List<NavItem>
        {
            new NavItem { Href = "/contact/me", Label = "Me" },
            new NavItem { Href = "/contact/email", Label = "Email" },
            new NavItem { Label = "-" },
            new NavItem { Href = "/contact/phone", Label = "Phone" },
        }
    },
    new NavItem { Href = "/login",    Label = "Sign In", Hide = "auth" },
    new NavItem { Href = "/profile",  Label = "Profile", Show = "auth" },
    new NavItem { Href = "/admin",    Label = "Admin",   Show = "role:Admin" },
});

When you add a UI Feature or validation example it uses the code API to automatically make it available to your App’s main menu, but the new project templates only populate it from appsettings.json.

Thank you for your help. I didn’t get your answer until now as my notifications settings weren’t set to email me until after being away for 10 minutes (the default settings). Somehow I missed your reply until today. Very useful information, as always. I have one more question related to this.

Since now the advised way is to no longer use the create-react-app server due to the nav bar and auth not working, is there still a way to run the react template with the create-react-server so during development, the react-error-overlay still displays? An example of the error-overlay is:

This error overlay is super useful, as any errors in my react code will automatically display in the browser, and when I click on the error, it takes me right to the line of code that has the error. But when served from Service Stack, this error overlay no longer works, as it’s a feature from the create-react-app. Of course, when using the create-react-app server, the Service Stack navigation does not work then (which is why you removed those instructions from the docs).

But is there still a way to access the nav and auth calls from ajax or another call ONLY during development (so both the react-error-overlays work and the nav and auth would still work). Then in production mode the app can run off of the ServiceStack server.

Would you be able to point out how I can convert the following (which is on the index page) to get these calls (the css, the svg, NAV_ITEMS, and AUTH as an ajax call so I can still use the create-react-app during development:

 <i hidden>
{{ 'buttons,svg-auth,app' | cssIncludes }}
{{ 'svg-icons'            | cssIncludes | svgFill('#008299') }}
{{#script}}
{{ '/js/hot-fileloader.js'    | onlyIfDebug | includeFile }}
NAV_ITEMS = {{ 'GetNavItems'  | execService | json }};
AUTH      = {{ 'Authenticate' | execService({ ifErrorReturn: "null" }) | json }};
{{/script}}
</i>

I think this would be super useful to have that error overlay still available during development that the create-react-app provides and still have your awesome nav and auth functionality. Thank you for your help.

You may want to maintain 2 different index pages that your Fallback Route returns, e.g:

public object Any(FallbackForClientRoutes request) => HostContext.DebugMode
  ? (object) new HttpResult(VirtualFileSources.GetFile("index.static.html"));
  : Request.GetPageResult("/");

So when Config.DebugMode=true it will return index.static.html otherwise returns the evaluated index.html.

Then in your new index.static.html you’ll remove all #Script code from the template, the cssIncludes just inlines stylesheets with the default format of /css/{name}.css so you can just add external references to those:

<link rel="stylesheet" href="/css/buttons.css">
<link rel="stylesheet" href="/css/svg-auth.css">
<link rel="stylesheet" href="/css/app.css">
<link rel="stylesheet" href="/css/svg-icons.css">

There’s no replacement for svgFill which does a dynamic fill replace, to match the behavior you’ll need to create a local copy of /css/svg-icons.css and manually change the svg fill colors yourself, although leaving it different will provide a visual clue which index.html that’s being served.

create-react-app should have their own hot-reloader, but you can also enable ServiceStack’s by including the script tag:

<script src="/js/hot-fileloader.js"></script>

The remaining 2 calls are just service calls of the same name:

(async () => {

    var client = new JsonServiceClient('/');
    window.NAV_ITEMS = await client.get(new GetNavItems());
    try {
      window.AUTH = await client.get(new Authenticate());
    } catch(e) {}

})();

As your index.html doesn’t have access to your /src modules where you can easily access the JsonServiceClient in shared/index.tsx it will be easier if you set a flag in your index.static.html like:

<script>LOAD_VIA_AJAX=true</script>

Then run the above code in your /shared/index.tsx:

if (global.LOAD_VIA_AJAX) { ... }

Otherwise you’ll need to pull in the UMD version of @servicestack/client which you can get from the react-lite project:

servicestack-client.umd.js

then copy in the JS compiled typed Authenticate DTOs (generated from: tsc dtos.ts):

Authenticate=function(){
  function n(n){ Object.assign(this,n) }
  return n.prototype.createResponse=function(){
  return new AuthenticateResponse
};
n.prototype.getTypeName=function(){return"Authenticate"},n}();
AuthenticateResponse=function(){function n(n){Object.assign(this,n)}return n}()

GetNavItems is defined in the @servicestack/client library

This is awesome, thank you. I finally got everything working related to getting the nav menu bar items while still serving from the create-react-app server. I’m using your first recommendation, with LOAD_VIA_AJAX=true.

To be complete here in case anyone else has trouble, window.NAV_ITEMS isn’t available when initialState is first set. However, when console logging window.NAV_ITEMS, this is available, but it’s available too late (after initial state was already loaded). For example, in the following code the nav property is defined using NAV_ITEMS from the window in initialState:

const initialState: State = {
  nav: global.NAV_ITEMS as GetNavItemsResponse,
  userSession: global.AUTH as AuthenticateResponse,
  userAttributes: UserAttributes.fromSession(global.AUTH),
};

and it’s initial state is pass through here in the StateProvider:

export const StateProvider = (props: any) => {
  const [state, dispatch] = useReducer( reducer, initialState );

  return (<StateContext.Provider value={{ state, dispatch }}>{props.children</StateContext.Provider>);
}

So the state.nav property shows up null even though window.NAV_ITEMS is available in the window. As a test, in the reducer signout case statement, I set the nav item property of state back to the window property for NAV_ITEMS, and it works when signing out (the nav items show up when you click the signout button after first logging in!! Now how to get this on initial load):

const reducer = (state: State, action: Action) => {
  switch (action.type) {
    case 'signout':
      return { nav: global.NAV_ITEMS as GetNavItemsResponse, userSession: null } as State;

  }
}

So to fix this, I just had add a member to the Action interface:

interface Action {
  type: 'signout' | 'signin' |'setNavBarItems'
  data?: AuthenticateResponse | any
}

Then created a function:

export const setNavBarItems = async (dispatch: Dispatch) => {
  dispatch({ type: 'setNavBarItems' });
};

Next, I had to add a new case statement in the reducer function:

    case 'setNavBarItems':
      return { ...state, nav: global.NAV_ITEMS } as State;

And finally, used UseEffects in the StateProvider, to update the nav bar items whenever this window property changes. Like so:

export const StateProvider = (props: any) => {
  const [state, dispatch] = useReducer( reducer, initialState );

  useEffect(() => {
    setNavBarItems( dispatch );

  }, [global.NAV_ITEMS]);

  return (<StateContext.Provider value={{ state, dispatch }}>{props.children}</StateContext.Provider>);
}

I hope this might help someone else. All of this is in the index.tsx file and running the nav and auth service calls in the same file.

Would you consider adding this back to the docs for people who would still like to use the create-react-app server in the React templates. I just think it’s an awesome developer experience to be able to have auth and nav working with ServiceStack while still having the error overlays working with the create-react-app server. So all works now, thanks again for your help,

–Tim

I forgot to add, with the above solution, in the JSX, you still need to check if state.nav exists before writing the JSX, such as:

{state.nav && <Navbar items={state.nav.results} attributes={state.userAttributes} />}

– wherever state.nav is being accessed. But at least with the above, you can now serve from the servicestack server in production and still get all the benefits of the servicestack server and the create-react-app in development.

I don’t want to make the templates more complicated than necessary, but if you add the changes to the files you’ve made in a GitHub Gist we can use the mix tool to be able to apply the changes locally with a single command, e.g:

$ mix create-react-app

Where it will overwrite the default template with your changes and enable using create-react-app.

Which could also be applied during project creation time, e.g:

$ web new react-spa+create-react-app ProjectName