How to pass information from a child functional component to a React parent?

I am developing an application with React with Hooks and functional components, and I came across the following logic: I need to render on screen the functional components, both with arrays of forms inside the main component, instead of I send the information to my API inside each component I found it more practical the child components pass the arrays to the parent component and the same send once, so I thought to pass as props a function that array in a state of the parent component, but every time I try to access this function from the child component of an error.

Parent components:

function Config(){

    const [registerData, setRegisterData] = useState([])
    const [deleteData, setDeleteData] = useState(null)

    function setRegister(dados){
        setRegisterData(dados)
    }

    function setDelete(dados){
        setDeleteData(dados)
    }

    console.log(registerData, deleteData)

    return(
        <div>
            <Register data={setRegister} />
            <br />
            <Delete data={setDelete} />
        </div>
    )
}
export default Config;

Child component:

function Register({props}){

    const [usuario, setUsuario] = useState('')
    const [telefone, setTelefone] = useState('')
    const [unidade, setUnidade] = useState('')
    const [departamento, setDepartamento] = useState('')
    const [cargo, setCargo] = useState('')


    const form = {usuario: usuario, telefone: telefone, unidade: unidade, departamento: departamento, cargo: cargo}

    props.setRegister(form);

    return(
        <div className="app">

            <center>
            <input type="text" onChange={(e) => setUsuario(e.target.value)} className="form-input" placeholder="Usuário" />
            <br />
            <br />
            <input type="text" onChange={(e) => setTelefone(e.target.value)} className="form-input" placeholder="Telefone" />
            <br />
            <br />
            <select>
                <option onChange={(e) => setUnidade(e.target.value)}>Selecione uma unidade</option>
                <option value="UB">UB</option>
                <option value="UG">UG</option>
                <option value="UM">UM</option>
                <option value="UP">UP</option>
                <option value="UV">UV</option>
                <option value="UL">UL</option>
            </select>
            <br />
            <br />
            <input type="text" onChange={(e) => setDepartamento(e.target.value)} className="form-input" placeholder="Departamento" />
            <br />
            <br />
            <input type="text" onChange={(e) => setCargo(e.target.value)} className="form-input" placeholder="Cargo" />
            <br /><br />
            <br /><br />
            <button className="btn btn-lg btn-dark">Cadastrar</button>
            </center>

        </div>
    )
} 

export default Register; 

The error that happens:

TypeError: Cannot read property 'setRegister' of undefined
Author: Luiz Felipe, 2020-06-08

1 answers

First of all, it's worth remembering that JSX is nothing more than a syntactic sugar for JavaScript. In this way, everything can still be represented as functions.

So when you do:

<Register data={setRegister} />

You are actually invoking the React.createElement method. Like this:

React.createElement(
  Register,
  {
    data: setRegister
  }
);

Being the first argument the component you want to create, the second the properties you want to pass to it and the third the children (children). As, in the example in question, the component there is no children, this parameter has been omitted. For more information, see the documentation.

With this in mind, it is also important to be aware that the properties (what we passed in the second argument to createElement) can be accessed by the first functional components parameter. Like this:

function Register(props) {
  console.log(props);
}

Thus, assuming the previous example, the above example would print to the browser console { data: setRegister }. See:

function setRegister() {
  // Do stuff.
}

function Register(props) {
  console.log(props);
  
  return null;
}

ReactDOM.render(
  // Mesmo que:
  // <Register data={setData} />
  React.createElement(
    Register,
    {
      data: setRegister
    }
  ),
  document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="app"></div>

So when you unstructures the properties like this:

function Register({ props }) {
  // ...
}

You want to get only the props property of the props objects that is passed to the first argument of Register by React. It doesn't make sense, since you haven't even passed props to the component Register:

<Register data={setData} />

Note that we pass data, but not props.

Therefore, to access the function setData, do:

// Note que não estamos mais desestruturando.
function Register(props) {
  console.log(props.data); // Irá imprimir a sua função.

  // Para chamar a função, faça:
  props.data();

  // ...
}

But realize that this name data doesn't make sense. It does not even help to identify what the property does. So I think it would be a little more "nice" to rename it to something like setRegister.

Then you will do:

function Register(props) {
  // Chamando...
  props.setRegister();

  // ...
}

<Register setRegister={setRegister} />

Suggested reading

 2
Author: Luiz Felipe, 2020-06-08 20:20:20