Search code examples
javascriptreactjsreact-hooksreact-propsreact-functional-component

React Props wont update in Child Components


Following Situation.

I have a functional Parent Component like this:

function TestAutomationTab() {
  const theme = createMuiTheme({
    typography: {
      htmlFontSize: 10,
      useNextVariants: true,
    },
  });

  const [szenarios, setSzenarios] = useState([]);
  const [filterSzenario, setFilterSzenario] = useState('ALL');
  const [data, setData] = useState([{}]);
  const [runAll, setRunAll] = useState(false);
  const [runAllButton, setRunAllButton] = useState('RUN ALL');

  useEffect(() => {
    fetchDistinctSzenarios();
    fetchTestfaelle();
  }, []);

  async function fetchDistinctSzenarios() {
    const response = await Api.getDistinctTestautoSzenarios();
    setSzenarios(response.data);
    setSzenarios(oldState => [...oldState, 'ALLE']);
  }

  function handleFilterChange(event) {
    setFilterSzenario(event.target.value);
    fetchTestfaelle();
  }

  async function fetchTestfaelle() {
    const response = await Api.getAllOeTestfaelle();
    response.data.forEach((e) => {
      e.status = 'wait';
      e.errorStatus = '';
      e.statusText = '-';
    });
    setData(response.data);
  }

 function sendSingleCase(id) {
    
     data.forEach((e) => {
      if(e.id === id){
        e.status = 'sending';
      }
     })

  }

  return (
    <React.Fragment>
      <MuiThemeProvider theme={theme}>
        <div style={styles.gridContainer}>
          <Upload />
          <TestautomationSzenarioFilter
          />
          <DocBridgePieChart />
          <div style={styles.uebersicht}>
            {filterSzenario.length ? <OeTestfallAccordion
              choosenFilter={filterSzenario}
              testData={data}
              runAll={runAll}
              sendSingleCase={sendSingleCase}
            /> : <div>Wähle Szenario</div>}
          </div>
        </div>
      </MuiThemeProvider>
    </React.Fragment>
  );
}

OeTestfallAccordion

function OeTestfallAccordion(props) {
  const data = props.testData;

  return (
    <React.Fragment>
      {data.map(e => (<OeTestfall
        key={e.id}
        szenario={e.szenario}
        testid={e.testfallid}
        json={e.json}
        status={e.status}
        runAll={props.runAll}
        errorStatus={e.errorStatus}
        statusText={e.statusText}
        sendSingleCase={props.sendSingleCase}
      />))}
    </React.Fragment>
  );
}

OeTestfall

function OeTestfall(props) {
  const { szenario, testid, json } = props;
  const [open, setOpen] = useState(false);

  function handleOpen(event) {
    event.stopPropagation();
    setOpen(true);
  }

  function handleClose() {
    setOpen(false);
  }

  return (
    <ExpansionPanel>
      <ExpansionPanelSummary expandIcon={<ExpandMoreOutlined />}>
        <OeTestfallSummary
          szenario={szenario}
          testid={testid}
          json={json}
          status={props.status}
          handleClose={handleClose}
          handleOpen={handleOpen}
          open={open}
          statusText={props.statusText}
          errorStatus={props.errorStatus}
          sendSingleCase={props.sendSingleCase}
        />
      </ExpansionPanelSummary>
      <ExpansionPanelDetails>
        <div>ForNoError</div>
      </ExpansionPanelDetails>
      <ExpansionPanelActions>
        <Button
          variant="outlined"
          color="primary"
        >
            Bearbeiten
        </Button>
        <Button
          variant="outlined"
          color="secondary"
        >
            Löschen
        </Button>
      </ExpansionPanelActions>
    </ExpansionPanel>
  );
}

OeTestfallSummery

function OeTestfallSummary(props) {
  const { handleOpen } = props;

  const [status, setStatus] = useState('');
  const [statusText, setStatusText] = useState('');
  const [errorStatus, setErrorStatus] = useState('');

  useEffect(() => {
    setErrorStatus(props.errorStatus);
    setStatusText(props.statusText);
    setStatus(props.status);
  }, []);


  return (
    <div style={styles.summaryWrapper}>
      <Typography align="center" variant="subtitle1">
            TestID: {props.testid}
      </Typography>
      <Typography align="center" variant="subtitle1" style={{ fontWeight: 'bold' }}>
        {props.szenario}
      </Typography>
      <Button
        size="small"
        variant="outlined"
        color="primary"
        onClick={handleOpen}
      >
            JSON
      </Button>
      <Tooltip title="VorneTooltip" style={styles.lightTooltip} placement="left">
        <Chip
          color="secondary"
          variant="outlined"
          label={status}
        />
      </Tooltip>
      <StatusChip
        status={errorStatus}
      />
      <OeJsonViewer json={JSON.parse(props.json)} open={props.open} handleClose={props.handleClose} stopEventPropagation />
      <Tooltip
        title="ToolTipTitel"
        style={styles.lightTooltip}
        placement="top"
      >
        <Chip
          color="primary"
          variant="outlined"
          label={statusText}
        />
      </Tooltip>
      <Button variant="contained" color="primary" onClick={() => props.sendSingleCase(props.testid)} >
            Run
      </Button>
      <Button variant="contained" color="primary" onClick={() => console.log(status)} >
            test
      </Button>
    </div>
  );
}

In my OeTestfallAccordion the prop testData does not update. If i try to console.log it inside my childComponent it has the old Value like before i execute the sendSinglecase function. What do i need to do, that i update the Data correctly that my child component gets notified that the props had changed and it has to rerender.

EDIT:

I tried some new things and can narrow down the problem. In my TestAutomationTab Component i send the whole data State to the OeTestfallAccordion Child Component. In this OeTestfallAccordion Component i split up the Array of Data which consists of multiple Objects like:

0: {id: 41, testfallid: 1, json: "{\"testCaseData\":{\"baseData\":{\"Check\":\"Thing…e\":\"alle\",\"tuwid\":\"2909\"}},\"testType\":\"Test\"}}", ID: null, businessId: null, …}
1: {id: 42, testfallid: 2, json: "{\"testCaseData\":{\"baseData\":{\"testfallid\":\"1…e\":\"alle\",\"tuwid\":\"2909\"}},\"testType\":\"Test\"}}", edcomAuftragsId: null, businessId: null, …}

When i hit the function sendSingleCase in my Parent Component TestAutomationTab i just change one single Parameter of the Object. The whole construct of Data keeps the same. The Child Component doesnt recognize that i changed something in the Object of Data.

But i dont know why? I also tried to useEffect on Props change in my Child COmponent when the props are changed. But it never gets executed even tho some attributes got updated inside the props.data.

function OeTestfallAccordion(props) {
  const testData = props.testData;
  const [data, setData] = useState(testData);

  useEffect(() => {
    setData(testData);
    console.log("triggered");
  }, [props]);
...
}

Solution

  • Okay things worked out a bit.

    I changed the sendSingleCase function to first Copy the whole state in a Temp variable. Change one Attribute inside an Object and then setData (inside useState) with the tempData Variable. So the whole State gets renewed and the child components recognize the change and rerender.

    But it seems not to be very fast. Always to copy the whole Data in a new Variable and then reassign it is very Ressource heavy. Is there a better solution?

      function sendSingleCase(id) {
        const tempState = [...data];
        tempState.forEach((e) => {
          if (e.testfallid === id) {
            e.status = "pressed";
            console.log(e.status);
          }
        });
        setData(tempState);
      }