Home > Software engineering >  how to update parent component when changing child component?
how to update parent component when changing child component?

Time:04-01

I have two components. A parent component which englobes table of items and an edit component which is opend when only one item is selected to be modified. this is the parent component:

import * as React from 'react';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Title from './Title';
import Grid from '@mui/material/Grid';
import Paper from '@mui/material/Paper';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import Container from '@mui/material/Container';
import { useState, useEffect } from "react";
import ReportDataService from "../services/report";
import FormDialogAdd from "./add-report";
import DeleteDialog from "./delete-report";

import FormDialogEdit from "./edit-report";




const ReportsList = props => {
  const [reports, setReports] = useState([]);
  // console.log("salut",reports)
  
  const retrieveReports = () => {
    ReportDataService.getAll()
      .then(response => {
        // console.log(response.data);
        setReports(response.data.reports);
        
      })
      .catch(e => {
        console.log(e);
      });
  };

  


// update dom after changes were made
  useEffect(() => {
    retrieveReports();
  }, []);


  return (
    <Box
    component="main"
    sx={{
      backgroundColor: (theme) =>
        theme.palette.mode === 'light'
          ? theme.palette.grey[100]
          : theme.palette.grey[900],
      flexGrow: 1,
      height: '100vh',
      overflow: 'auto',
    }}
  >
    <Toolbar />
    <Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>


      <Grid container spacing={3}>
        {/* Recent Orders */}
        <Grid item xs={12}>
          <Paper sx={{ p: 2, display: 'flex', flexDirection: 'column' }}>
      <React.Fragment>
      <Title>Reports</Title>
      <Table size="small">
        <TableHead>
          <TableRow>
            <TableCell>name</TableCell>
            <TableCell>ecu</TableCell>
            <TableCell>category</TableCell>
            <TableCell>lastModified</TableCell>
            <TableCell>comment</TableCell>
            <TableCell>reviewed</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {reports.map((report, index) => (
            <TableRow key={index}>
              <TableCell required>{report.name}</TableCell>
              <TableCell>{report.ecu}</TableCell>
              <TableCell>{report.category}</TableCell>
              <TableCell>{report.lastModified.slice(0,10)}</TableCell>
              <TableCell>{report.comment}</TableCell>
              <TableCell>{report.reviewd ? "True" : "False"}</TableCell>
              <Box sx={{ display: 'flex' }}>
                <FormDialogEdit reportId={report._id}/>
                <DeleteDialog reportId={report._id} />
              </Box>


            </TableRow>
          ))}
        </TableBody>
      </Table>
      

    </React.Fragment>
  
  </Paper>
  
  <FormDialogAdd/>
</Grid>
</Grid>



</Container>
</Box>
  );
};

export default ReportsList;

and this code is for the child component for edit:


import * as React from 'react';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import ListItemButton from '@mui/material/ListItemButton';
import FormControlLabel from '@mui/material/FormControlLabel';
import Switch from '@mui/material/Switch';
import ListItemText from '@mui/material/ListItemText';
import DialogTitle from '@mui/material/DialogTitle';
import Fab from '@mui/material/Fab';
import EditIcon from '@mui/icons-material/Edit';
import ReportDataService from "../services/report";
import Box from '@mui/material/Box';
import { useState, useEffect } from "react";


export default function FormDialogEdit(props) {

  const [open, setOpen] = React.useState(false);

  const handleClose = () => {
    setOpen(false);
  };


  const getSingleReport = (reportId) => {
    setOpen(true);
    // console.log(reportId)
    ReportDataService.get(reportId)
      .then(response => {
        // console.log("data",response.data);
        setReport(response.data);
        
      })
      .catch(e => {
        console.log(e);
      });
  };

  let initialReportState = ""
  const [report, setReport] = useState(initialReportState);


// begins always false=> not convincing param=> should be updated like reviewd
  const [manualTest, setManualTest] = React.useState(false);
  const handleChangeTestManual = (event) =>{
    setManualTest(event.target.checked)
  }
  

  const [inputs, setInputs] = useState({});
  console.log(inputs);
  const handleChange = e => setInputs(prevState => ({ ...prevState, [e.target.name]: e.target.value }));
  const handleChangeReviewed= e => setInputs(prevState => ({ ...prevState, [e.target.name]: e.target.checked }));
  // console.log("hi",inputs)


  const saveReport = () => {

    ReportDataService.updateReport(inputs)
    .then(response => {
      
      // console.log(response.data);
    })
    .catch(e => {
      console.log(e);
    });
  };


  useEffect(() => {
    setInputs(report)
  }, [report]);

  return (
    <div>
      <Fab size="small" sx={{ m: 1}} color="primary" aria-label="edit" onClick={() => getSingleReport(props.reportId)}>
        <EditIcon  />
      </Fab>
      <Dialog open={open} onClose={handleClose}>
        <DialogTitle>Edit report</DialogTitle>
        <DialogContent>
          <DialogContentText>
            You can see here all informations about a report and modify parameters
          </DialogContentText>
          <Box sx={{ border: 1, borderColor: 'grey.500', borderRadius: 2, marginTop:2}}>
          <ListItemButton>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Name"
            name="name"
            type="text"
            value={inputs.name}
            onChange={handleChange}
            fullWidth
            variant="standard"
          />
          </ListItemButton>
          <ListItemButton>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Ecu"
            name="ecu"
            value={inputs.ecu}
            onChange={handleChange}
            type="text"
            fullWidth
            variant="standard"
          />
          </ListItemButton>
          <ListItemButton>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Category"
            name="category"
            value={inputs.category}
            onChange={handleChange}
            type="text"
            fullWidth
            variant="standard"
          />
          </ListItemButton>
          <ListItemButton>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Comment"
            name="comment"
            value={inputs.comment}
            onChange={handleChange}
            type="text"
            fullWidth
            variant="standard"
          />
          </ListItemButton>
          <ListItemButton>
          <FormControlLabel  
          control={
          <Switch
          checked= {manualTest}
          onChange={handleChangeTestManual}
          />
            
          
          
          } 
          label="Manual test" />
          </ListItemButton>

          <ListItemButton>
          <FormControlLabel       
          control={
          <Switch
          checked= {inputs.reviewd}
          name="reviewd"
          onChange={handleChangeReviewed}
          />
            
          
          
          } label="Reviewed" />
          </ListItemButton>
          </Box>

          <Box sx={{ border: 1, borderColor: 'grey.500', borderRadius: 2, marginTop:2}}>
          <ListItemButton>
          <ListItemText primary="Last Modified by" secondary={report.lastModifiedBy}  />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Last Modified" secondary={report.lastModified}  />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Rating" secondary={report.rating} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Error injection" secondary={report.errorInjection} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Simulation" secondary={report.simulation} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Test cases" secondary={report.testCases} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Last run" secondary={report.lastRun} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="File" secondary={report.file} />
          </ListItemButton>
          </Box>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose}>Cancel</Button>

          <Button onClick={() =>{
            saveReport();
            handleClose();
          }}>
            Update</Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}


I want to be able to update the parent component when clicking on the update button in the child component. In other world I want the parent component to refresh and modify changes directly. The solution from what i saw is with useEffect but i didn`t know how to use it. So can anyone help? How to notify the parent that a changment has been made so that the table should be updated as soon as the update button in the child is clicked?

CodePudding user response:

add a new prop to the child component which passes setReport, this can then be called within the child component and the state will update within the parent making it refresh

<FormDialogEdit reportId={report._id} setReport={setReport}/>

CodePudding user response:

Add a props into your child component
like this props.func('updated');

import * as React from 'react';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import ListItemButton from '@mui/material/ListItemButton';
import FormControlLabel from '@mui/material/FormControlLabel';
import Switch from '@mui/material/Switch';
import ListItemText from '@mui/material/ListItemText';
import DialogTitle from '@mui/material/DialogTitle';
import Fab from '@mui/material/Fab';
import EditIcon from '@mui/icons-material/Edit';
import ReportDataService from "../services/report";
import Box from '@mui/material/Box';
import { useState, useEffect } from "react";


export default function FormDialogEdit(props) {
  props.func('updated');
  const [open, setOpen] = React.useState(false);

  const handleClose = () => {
    setOpen(false);
  };


  const getSingleReport = (reportId) => {
    setOpen(true);
    // console.log(reportId)
    ReportDataService.get(reportId)
      .then(response => {
        // console.log("data",response.data);
        setReport(response.data);
        
      })
      .catch(e => {
        console.log(e);
      });
  };

  let initialReportState = ""
  const [report, setReport] = useState(initialReportState);


// begins always false=> not convincing param=> should be updated like reviewd
  const [manualTest, setManualTest] = React.useState(false);
  const handleChangeTestManual = (event) =>{
    setManualTest(event.target.checked)
  }
  

  const [inputs, setInputs] = useState({});
  console.log(inputs);
  const handleChange = e => setInputs(prevState => ({ ...prevState, [e.target.name]: e.target.value }));
  const handleChangeReviewed= e => setInputs(prevState => ({ ...prevState, [e.target.name]: e.target.checked }));
  // console.log("hi",inputs)


  const saveReport = () => {

    ReportDataService.updateReport(inputs)
    .then(response => {
      
      // console.log(response.data);
    })
    .catch(e => {
      console.log(e);
    });
  };


  useEffect(() => {
    setInputs(report)
  }, [report]);

  return (
    <div>
      <Fab size="small" sx={{ m: 1}} color="primary" aria-label="edit" onClick={() => getSingleReport(props.reportId)}>
        <EditIcon  />
      </Fab>
      <Dialog open={open} onClose={handleClose}>
        <DialogTitle>Edit report</DialogTitle>
        <DialogContent>
          <DialogContentText>
            You can see here all informations about a report and modify parameters
          </DialogContentText>
          <Box sx={{ border: 1, borderColor: 'grey.500', borderRadius: 2, marginTop:2}}>
          <ListItemButton>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Name"
            name="name"
            type="text"
            value={inputs.name}
            onChange={handleChange}
            fullWidth
            variant="standard"
          />
          </ListItemButton>
          <ListItemButton>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Ecu"
            name="ecu"
            value={inputs.ecu}
            onChange={handleChange}
            type="text"
            fullWidth
            variant="standard"
          />
          </ListItemButton>
          <ListItemButton>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Category"
            name="category"
            value={inputs.category}
            onChange={handleChange}
            type="text"
            fullWidth
            variant="standard"
          />
          </ListItemButton>
          <ListItemButton>
          <TextField
            autoFocus
            margin="dense"
            id="name"
            label="Comment"
            name="comment"
            value={inputs.comment}
            onChange={handleChange}
            type="text"
            fullWidth
            variant="standard"
          />
          </ListItemButton>
          <ListItemButton>
          <FormControlLabel  
          control={
          <Switch
          checked= {manualTest}
          onChange={handleChangeTestManual}
          />
            
          
          
          } 
          label="Manual test" />
          </ListItemButton>

          <ListItemButton>
          <FormControlLabel       
          control={
          <Switch
          checked= {inputs.reviewd}
          name="reviewd"
          onChange={handleChangeReviewed}
          />
            
          
          
          } label="Reviewed" />
          </ListItemButton>
          </Box>

          <Box sx={{ border: 1, borderColor: 'grey.500', borderRadius: 2, marginTop:2}}>
          <ListItemButton>
          <ListItemText primary="Last Modified by" secondary={report.lastModifiedBy}  />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Last Modified" secondary={report.lastModified}  />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Rating" secondary={report.rating} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Error injection" secondary={report.errorInjection} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Simulation" secondary={report.simulation} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Test cases" secondary={report.testCases} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="Last run" secondary={report.lastRun} />
          </ListItemButton>
          <ListItemButton>
          <ListItemText primary="File" secondary={report.file} />
          </ListItemButton>
          </Box>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose}>Cancel</Button>

          <Button onClick={() =>{
            saveReport();
            handleClose();
          }}>
            Update</Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}

**And in Parent component ** use that props like this

import Container from '@mui/material/Container';
import { useState, useEffect } from "react";
import ReportDataService from "../services/report";
import FormDialogAdd from "./add-report";
import DeleteDialog from "./delete-report";

import FormDialogEdit from "./edit-report";




const ReportsList = props => {
  const [reports, setReports] = useState([]);
  // console.log("salut",reports)
  
  const retrieveReports = () => {
    ReportDataService.getAll()
      .then(response => {
        // console.log(response.data);
        setReports(response.data.reports);
        
      })
      .catch(e => {
        console.log(e);
      });
  };

  


// update dom after changes were made
  useEffect(() => {
    retrieveReports();
  }, []);


  return (
    <Box
    component="main"
    sx={{
      backgroundColor: (theme) =>
        theme.palette.mode === 'light'
          ? theme.palette.grey[100]
          : theme.palette.grey[900],
      flexGrow: 1,
      height: '100vh',
      overflow: 'auto',
    }}
  >
    <Toolbar />
    <Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>


      <Grid container spacing={3}>
        {/* Recent Orders */}
        <Grid item xs={12}>
          <Paper sx={{ p: 2, display: 'flex', flexDirection: 'column' }}>
      <React.Fragment>
      <Title>Reports</Title>
      <Table size="small">
        <TableHead>
          <TableRow>
            <TableCell>name</TableCell>
            <TableCell>ecu</TableCell>
            <TableCell>category</TableCell>
            <TableCell>lastModified</TableCell>
            <TableCell>comment</TableCell>
            <TableCell>reviewed</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {reports.map((report, index) => (
            <TableRow key={index}>
              <TableCell required>{report.name}</TableCell>
              <TableCell>{report.ecu}</TableCell>
              <TableCell>{report.category}</TableCell>
              <TableCell>{report.lastModified.slice(0,10)}</TableCell>
              <TableCell>{report.comment}</TableCell>
              <TableCell>{report.reviewd ? "True" : "False"}</TableCell>
              <Box sx={{ display: 'flex' }}>
                <FormDialogEdit reportId={report._id} func={retrieveReports}/>
                <DeleteDialog reportId={report._id} />
              </Box>


            </TableRow>
          ))}
        </TableBody>
      </Table>
      

    </React.Fragment>
  
  </Paper>
  
  <FormDialogAdd/>
</Grid>
</Grid>



</Container>
</Box>
  );
};

export default ReportsList;
  • Related