React Route and Windows

Hi I’m new to docker and trying to develop an extension as a learning experience. I’m using React Routes to switch between the main pages of the extension but after installing my extension the routing doesn’t seem to be working, nothing renders when I click the buttons that triggers the redirect. I’m working with a few other people and it’s working on their system (Mac) but not on mine(Windows 10).

Let me know if you have any suggestions to get the routing working.

…Dockerfile…

FROM golang:1.19-alpine AS builder
ENV CGO_ENABLED=0
WORKDIR /backend
COPY backend/go.* .
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go mod download
COPY backend/. .
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go build -trimpath -ldflags="-s -w" -o bin/service

FROM --platform=$BUILDPLATFORM node:18.12-alpine3.16 AS client-builder
WORKDIR /ui
# cache packages in layer
COPY ui/package.json /ui/package.json
COPY ui/package-lock.json /ui/package-lock.json
RUN --mount=type=cache,target=/usr/src/app/.npm \
    npm set cache /usr/src/app/.npm && \
    npm ci
# install
COPY ui /ui
RUN npm run build

FROM alpine
LABEL org.opencontainers.image.title="dockular" \
    org.opencontainers.image.description="our osp project" \
    org.opencontainers.image.vendor="danger-noodle-ptri13" \
    com.docker.desktop.extension.api.version="0.3.4" \
    com.docker.extension.screenshots="" \
    com.docker.desktop.extension.icon="" \
    com.docker.extension.detailed-description="" \
    com.docker.extension.publisher-url="" \
    com.docker.extension.additional-urls="" \
    com.docker.extension.categories="" \
    com.docker.extension.changelog=""

COPY --from=builder /backend/bin/service /
COPY docker-compose.yaml .
COPY metadata.json .
COPY docker.svg .
COPY --from=client-builder /ui/build ui
CMD /service -socket /run/guest-services/backend.sock

…Makefile…

IMAGE?=my/awesome-extension
TAG?=latest

BUILDER=buildx-multi-arch

INFO_COLOR = \033[0;36m
NO_COLOR   = \033[m

build-extension: ## Build service image to be deployed as a desktop extension
	docker build --tag=$(IMAGE):$(TAG) .

install-extension: build-extension ## Install the extension
	docker extension install $(IMAGE):$(TAG)

update-extension: build-extension ## Update the extension
	docker extension update $(IMAGE):$(TAG)

prepare-buildx: ## Create buildx builder for multi-arch build, if not exists
	docker buildx inspect $(BUILDER) || docker buildx create --name=$(BUILDER) --driver=docker-container --driver-opt=network=host

push-extension: prepare-buildx ## Build & Upload extension image to hub. Do not push if tag already exists: make push-extension tag=0.1
	docker pull $(IMAGE):$(TAG) && echo "Failure: Tag already exists" || docker buildx build --push --builder=$(BUILDER) --platform=linux/amd64,linux/arm64 --build-arg TAG=$(TAG) --tag=$(IMAGE):$(TAG) .

help: ## Show this help
	@echo Please specify a build target. The choices are:
	@grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "$(INFO_COLOR)%-30s$(NO_COLOR) %s\n", $$1, $$2}'

.PHONY: help

…main.tsx…

import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import CssBaseline from "@mui/material/CssBaseline";
import { DockerMuiThemeProvider } from "@docker/docker-mui-theme";

import { App } from './App';

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <React.StrictMode>
    <BrowserRouter>
    {/*
      If you eject from MUI (which we don't recommend!), you should add
      the `dockerDesktopTheme` class to your root <html> element to get
      some minimal Docker theming.
    */}
    <DockerMuiThemeProvider>
      <CssBaseline />
      <App />
    </DockerMuiThemeProvider>
    </BrowserRouter>
  </React.StrictMode>
);

…App.tsx…

import React from 'react';
import { Link } from "react-router-dom"
import { Route, Routes } from "react-router"
import Button from '@mui/material/Button';
import { createDockerDesktopClient } from '@docker/extension-api-client';
import { Divider, Stack, TextField, Typography } from '@mui/material';
import { Metrics } from "./metrics/components/cpu-ram"
import { Prune } from "./prune/prune"


// Note: This line relies on Docker Desktop's presence as a host application.
// If you're running this React app in a browser, it won't work properly.



const App = () => {
  // {"stdout":"{\"BlockIO\":\"7.33MB / 4.1kB\",\"CPUPerc\":\"0.00%\",\"Container\":\"772867bb9f60\",\"ID\":\"772867bb9f60\",\"MemPerc\":\"0.19%\",\"MemUsage\":\"14.9MiB / 7.657GiB\",\"Name\":\"gallant_banzai\",\"NetIO\":\"9.5kB / 0B\",\"PIDs\":\"11\"}\n{\"BlockIO\":\"94.7MB / 21.2MB\",\"CPUPerc\":\"0.51%\",\"Container\":\"f5acb0c87304\",\"ID\":\"f5acb0c87304\",\"MemPerc\":\"2.64%\",\"MemUsage\":\"206.7MiB / 7.657GiB\",\"Name\":\"jovial_mccarthy\",\"NetIO\":\"9.19kB / 0B\",\"PIDs\":\"32\"}\n","stderr":""}
  // const client = createDockerDesktopClient();

  // function useDockerDesktopClient() {
  //   return client;
  // }
    const [response, setResponse] = React.useState<string>();
    const ddClient = createDockerDesktopClient();
  
    const fetchAndDisplayResponse = async () => {

    const result = await ddClient.docker.cli.exec("ps", [
      "--all",
      // "--no-trunc",
      "--format",
      '"{{json .}}"',
    ]);

      setResponse(JSON.stringify(result))
  };

  return (
    <>
      <Stack
        direction="row"
        justifyContent="center"
        alignItems="center"
        spacing={8}
        sx= {{ pt : 4, pb : 8}}
      >
        <Typography variant="h3">Test</Typography>
        <Button variant="contained">Home</Button>

        <Button variant="contained">
          <Link to = {'/metrics'}> 
            {'Metrics'}
          </Link>
        </Button>

        <Button variant="contained">
          <Link to = {'/prune'}> 
            {'Prune'}
          </Link>
        </Button>

      </Stack>

      <Routes>
      {/* <Route path ="/" ><h1>Welcome</h1></Route> */}
        <Route path ="/metrics" element = {<Metrics />}/>
        <Route path ="/prune" element = {<Prune />}/>
      </Routes>
    </>
  );
}

export {App}

prune.tsx

import React, {useEffect} from 'react';
import Button from '@mui/material/Button';
import { createDockerDesktopClient } from '@docker/extension-api-client';
import { Box, Container } from '@mui/material';
import { blueGrey, red} from '@mui/material/colors';
import {  Stack } from "@mui/material";
import { DataGrid, GridRowsProp, GridColDef, gridPageSelector, gridPageCountSelector, useGridApiContext, useGridSelector, } from '@mui/x-data-grid';
import { useGridApiRef } from '@mui/x-data-grid';


const client = createDockerDesktopClient();

function useDockerDesktopClient() {
  return client;
}

export function Prune() {
  const [response, setResponse] = React.useState<string>('dangling-images');
  const [prune, setPrune] = React.useState<string>('');
  //useRef?
  const [containers, setContainers] = React.useState<any[]>([]);

  const ddClient = useDockerDesktopClient();




  //state for grid
  // const apiRef = useGridApiContext();
  // const paginationModel = useGridSelector(apiRef, gridPaginationModelSelector);
  const apiRef = useGridApiRef();
  function testApi(){
    console.log(apiRef.current.getSelectedRows())
  }

  useEffect(()=>{
     if (response === 'dangling-images'){
        //docker images --filter "dangling=true"
        ddClient.docker.cli.exec('images', ['--format', '"{{json .}}"', '--filter', "dangling=true"])
        .then((result) => {
          setContainers(result.parseJsonLines());
        });
    } else {
        ddClient.docker.cli.exec('ps', ['--all', '--format', '"{{json .}}"', '--filter', "status=exited"])
        .then((result) => {
          setContainers(result.parseJsonLines());
        });
    }
  },[response])

  const rows: GridRowsProp = containers.map((image) => ({
    id:image.ID,
    col1:image.Size,
    col2: response === 'dangling-images' ?image.CreatedSince : image.RunningFor,
    col3: response === 'dangling-images' ? image.Tag : image.Status
  }));

  const columns: GridColDef[] = [
    { field: 'id', headerName: 'ID', width: 150 },
    { field: 'col1', headerName: 'Size', width: 150 },
    { field: 'col2', headerName: 'Created', width: 150 },
    { field: 'col3', headerName: response === 'dangling-images'? 'Tag' : 'Status' , width: 150 }
  ];
  
  return (
    <>
        <Container
            sx={{
                width: '95vw',
                height: '85vh',
                bgcolor: blueGrey[50],
                display: 'flex',
                flexDirection: 'column',
                border:2,
                borderColor:'primary.main'

            }}
        >
          <Box 
          sx={{
            width: '90vw',
            height: '40vh',
            bgcolor: blueGrey[50],
            display: 'flex',
            justifyContent:'space-around',
            marginTop:4

        }}>
            <Box 
            sx={{
                bgcolor: blueGrey[50],
                width:'25%', 
                height:'90%',
                borderRadius: 2,
                border:2,
                borderColor:'primary.main'
            }}
            >
            <Stack>
                <Button variant="contained" onClick={()=>{setResponse('dangling-images')}} sx={{
                    m:2,
                    p: 1,
                    borderRadius: 2
                    }}>
                    Dangling Images
                </Button>
                <Button variant="contained" onClick={()=>{setResponse('unused-containers')}} sx={{
                    m:2,
                    p: 1,
                    borderRadius: 2
                }}>
                    UnUsed Containers
                </Button>
            </Stack>
            </Box>

            <Box sx={{
                width:'70%', 
                height:'90%',
                bgcolor: blueGrey[50],
                borderRadius: 2,
                border:2,
                borderColor:'primary.main',
                overflow: 'auto'
                }}>
                <DataGrid rows={rows} columns={columns} checkboxSelection apiRef={apiRef}/>
            </Box>

          </Box>
          <Box sx={{
            width: '90vw',
            height: '40vh',
            bgcolor: blueGrey[50],
            display: 'flex',
            // alignItems:'center',
            justifyContent:'space-around',
            // border:2,
            // borderColor:'primary.main'
            // marginTop:2

        }}>
          <Box
          sx={{
            bgcolor: blueGrey[50],
            width:'25%', 
            height:'90%',
            borderRadius: 2,
            border:2,
            borderColor:'primary.main'
        }}> 
          <Stack>
                <Button variant="contained" color='error' onClick={()=>{setPrune('prune-all')}} sx={{
                    m:2,
                    p: 1,
                    borderRadius: 2
                    }}>
                    Prune All
                </Button>
                
                <Button variant="contained" color='error' onClick={()=>{setPrune('prune-selected')}} sx={{
                    m:2,
                    p: 1,
                    borderRadius: 2
                    }}>
                    Prune Selected
                </Button>
                <Button variant="contained" color='error' onClick={()=>{console.log('Scheduled Prune Pressed'); testApi();}} sx={{
                    m:2,
                    p: 1,
                    borderRadius: 2
                }}>
                    Scheduled Prune
                </Button>
            </Stack>
          </Box>


          <Box
          sx={{
            width:'70%', 
            height:'90%',
            bgcolor: blueGrey[50],
            borderRadius: 2,
            border:2,
            borderColor:'primary.main',
        }}> Chart 
          </Box>
          </Box>
        </Container>
    </>
  );
}

Have you checked with the React Devtools Profiler tool that the components are realy not (re-)rendered?

Are there no error messages in the browser console?