Hi,
I am trying to get this grid to display the TEST message in the detail when I click the expander against the row. Everything looks to be working as expected (I am using state.fetchErrorMsg to provide debug text to prove that state is changing and triggering re-rendering when necessary, I can also confirm that the expander toggles between + and - as expected).
Any ideas what I am doing wrong here? I suspect it is something to do with how I am handling the promise that returns the data, since I can get this working when I use locally built static arrays instead of the fetch.
Cheers
Mike
import React, { useEffect, useState } from 'react';
import { UserInfo, TcbObjMilestoneGrp, TcbObjInfo } from '../../models/models';
import { loadingDiv } from '../../functions/componentFunctions';
import { Grid, GridColumn, GridExpandChangeEvent,  GridDetailRowProps } from '@progress/kendo-react-grid';
import './TcbObjMilestones.css';
type TcbObjMilestonesProps = {
    tcbObj: TcbObjInfo;
    userInf: UserInfo;
}
type TcbObjMilestonesState = {
    milestoneList: TcbObjMilestoneGrp[];
    milestonesFetched: boolean;
    fetchError: boolean;
    fetchErrorMsg: string;
    fetchInProg: boolean;
}
function TcbObjMilestones(props: TcbObjMilestonesProps) { 
    const [state, setState] = useState<TcbObjMilestonesState>({
        milestoneList: [],
        milestonesFetched: false,
        fetchError: false,
        fetchErrorMsg: '',
        fetchInProg: false
    });
    useEffect(() => {
        state.fetchErrorMsg = 'FetchInProg';
        state.fetchInProg = true;
        setState({ ...state});
        let url = props.userInf.currProject.apiUrl + '/api/details/GetTcbObjMilestones';
        fetch(url, { method: 'POST', body: JSON.stringify(props.tcbObj), headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + props.userInf.token } })
            .then(resp => resp.json())
            .then(res => {
                switch (res.callStatus) {
                    case "OK":
                        let ml: Array<TcbObjMilestoneGrp> = res.results;
                        state.fetchErrorMsg = 'Got MilestoneData '
                        state.milestoneList = ml;
                        state.milestonesFetched = true;
                        state.fetchInProg = false;
                        setState({ ...state });
                        break;
                    case "UNAUTH":
                        let uInf: UserInfo = { ...props.userInf, isAuthorised: false };
                        state.fetchInProg = false;
                        setState({ ...state });
                        break;
                    default:
                        state.fetchInProg = false;
                        state.fetchError = true;
                        state.fetchErrorMsg = res.callStatusMessage;
                        setState({ ...state });
                }
            })
            .catch(err => {
                state.fetchInProg = false;
                state.fetchError = true;
                state.fetchErrorMsg = 'Error fetching Item Details - ' + err.toString();
                setState({ ...state });
            });
    }, []);
  
    const expandGrpChange = (e: GridExpandChangeEvent) => {
        let dr: TcbObjMilestoneGrp = e.dataItem;
        let ml = state.milestoneList;
        let mlg = ml.find(x => x.milestoneId === dr.milestoneId)!;
        mlg.isExpanded = !e.dataItem.isExpanded;
   
        state.fetchErrorMsg = 'Expanded ' + mlg.isExpanded;
        setState({ ...state});
    }
    const renderMilestoneDetails = (e: GridDetailRowProps) => {
        return (<section><p>TEST</p></section>);
    };
     if (state.fetchInProg)
        return loadingDiv();
        let formatStr = "{0:dd-MMM-yyyy HH:mm }";
        return (
            <>
            <p>{state.fetchErrorMsg}</p>
            <Grid
                data={state.milestoneList}
                detail={renderMilestoneDetails}
                expandField="isExpanded"
                onExpandChange={expandGrpChange}
                resizable
            >
                <GridColumn field="milestoneDesc" title="Milestone" className="TcbObjMilestoneGridMainCell" />
                <GridColumn field="latestMilestoneDate" title="Latest" format={formatStr} width="120px" className="TcbObjMilestoneGridCell" />
                <GridColumn field="latestSource" title="Source" width="90px" className="TcbObjMilestoneGridCell" />
            </Grid>
            </>
        );
    }
    
export default TcbObjMilestones;

I managed to solve this.
My mistake was to try to save the data as part of the other state variables.
Once I created a separate useState to handle the data array it all worked perfectly.
Cheers
Mike