0

I have functional components, a table parent, and row child. The child controls are initiated by data passed from parent. The controls use useState hook to update the individual items. So theirs initial dataRows array in the parent, and current states in individual rows. Now I want to add a Save button below the table on the Parent, to persist changes. I understand that I can pass callback to Child, but what about the fact that the action is executed on the Parent and needs data from multiple Children? I could potentially execute save for initial rows, but I want to run it in batch for all rows present, so it needs to be done on parent, and initiated from the parent.

Parent:

export default function RozliczeniaFaktur ({web, context, formType}: IRozliczeniaFakturProps) {
    const [dataRows, setDataRows] = React.useState(emptyRowArr);
    const [isError, setIsError] = React.useState(false);
    const [errorMessage, setErrorMessage] = React.useState(null);

[...]
    
    return (
      
      <div className={styles.rozliczeniaFaktur}>
      <Stack tokens={stackTokens}>
      {isError && <MessageBar messageBarType={MessageBarType.error} isMultiline={false} >
        {errorMessage}
      </MessageBar>} 
      <div className={styles.dataTable}>
        <div className={styles.scrollContainer}>
        <table id='rozliczenia' className={styles.table}>
          <thead>
            <tr>
[...]
            </tr>
          </thead>
          <tbody>
             {dataRows && 
                dataRows.map((dataRow, i) => {
                  return <TableRowControl web={web} key={i} Context={context} RowId={i}  FormType={formType}
                    onAddRowCallback={addRow} onDeleteRowCallback={delRow}  />
                })
              }
          </tbody>
        </table>
        </div>
        <div>

        </div>
      </div>
      </Stack>
    </div>
    );
  
}

Child:

const TableRowControl = ({RowId, Context, PozFaktury, FormType, onAddRowCallback, onDeleteRowCallback, web, 
    onErrorCallback, OrganizacjaDictionary, VatDictionary, MpkDictionary, KontoDictionary, ZastepstwaDictionary,
    ZaliczkaDictionary} : ITableRowControlProps) => {   
    const [oddzialRozliczenia, setOddzialRozliczenia] = React.useState<number | string>(PozFaktury.OddzialRozliczenia);
    const [dataSprzedazy, setDataSprzedazy] = React.useState<Date>(PozFaktury.DataSprzedazy);
    const [dataOtrzymania, setDataOtrzymania] = React.useState<Date>(PozFaktury.DataOtrzymania);
    const [vat, setVat] = React.useState<string | number>(PozFaktury.StawkaVAT);
    const [nip, setNip] = React.useState<string>(PozFaktury.NIPDostawcy);
    const [waluta, setWaluta] = React.useState<string | number>(PozFaktury.Waluta);
    const [nrDok, setNrDok] = React.useState<string>(PozFaktury.NRDokumentu);
    const [kwota, setKwota] = React.useState<string>(PozFaktury.KwotaBrutto.toString())
    const [mpk, setMpk] = React.useState<string | number>(PozFaktury.MPK);
    const [konto, setKonto] = React.useState<string | number>(PozFaktury.KontoGL);
    const [magazyn, setMagazyn] = React.useState(PozFaktury.Magazyn);
    const [nrPz, setNrPz] = React.useState(PozFaktury.NrPZ);
    const [opis, setOpis] = React.useState(PozFaktury.Opis);
    const [zaliczka, setZaliczka] = React.useState<string | number>(PozFaktury.Zaliczka);
    const [aprobata1status, setAprobata1status] = React.useState(PozFaktury.Aprobata1Status);
    const [aprobata2status, setAprobata2status] = React.useState(PozFaktury.Aprobata2Status);
    const [komentarz, setKomentarz]



    return(
        <tr>
[...]
        </tr>
        );
}

export default TableRowControl;
n0e
  • 309
  • 3
  • 12
  • If not not mistaken, the save button needs to save values from each of the child components (rows). But, the child components contain state, correct? If so, couldn’t the state be lifted to parent (where save button exists)? – Nathan Hall Mar 13 '21 at 22:33
  • @NathanHall yeah, generally I could lift it by callbacks, but how to initiate it from the parent, where the button is? – n0e Mar 14 '21 at 09:45
  • you cannot access the state from a child using button click event. There is rule in react, “props down and events up”. Check this out https://jasonformat.com/props-down-events-up/ – Nathan Hall Mar 14 '21 at 09:52
  • @NathanHall ok, so perhaps I need to redesign it, change the approach - but to what? I would have to use useState not with local const in the child, but with some object on the parent (to maintain current state of all rows on the parent, and be able to acces it when pressing submit). Is that even possible? – n0e Mar 14 '21 at 10:36
  • absolutely possible. And, it’s much easier when using library such as Formik. – Nathan Hall Mar 14 '21 at 10:38
  • One way to manage is by assigning a name to each input. The change handler could then pull the name and value from the event. Check out this as an example. https://github.com/jaredpalmer/formik-alicante/blob/master/src/examples/MiniFormik.js – Nathan Hall Mar 14 '21 at 10:41
  • You can move state to the parent (be careful not to re-render all rows when just one changes though) or use a store (e.g. Redux). If you really want to access child components state from the parent you can try something like this (but Redux seems a better idea in your case) https://stackoverflow.com/questions/66445560/how-does-one-trigger-an-action-in-a-child-functional-component-in-react/ – Nadia Chibrikova Mar 14 '21 at 12:27

1 Answers1

0

Try using the useImperativeHandle hook, it might help you, its about passing methods and stuff up from your child to your parent for your parent to use them.

Dremiq
  • 335
  • 3
  • 10
  • I don’t think this would give a parent access to child state through button click event. Maybe you could provide example code? The button lives in parent, and state lives in child (from my understanding of the question) – Nathan Hall Mar 14 '21 at 09:55